Move TLS auth into separate file ("Daniel P. Berrange")
authoraliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162>
Fri, 6 Mar 2009 20:27:23 +0000 (20:27 +0000)
committeraliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162>
Fri, 6 Mar 2009 20:27:23 +0000 (20:27 +0000)
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>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6723 c046a42c-6fe2-441c-8c8c-71466251a162

Makefile
vnc.c
vnc.h

index 092c108..e8d1a5c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -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 --git a/vnc.c b/vnc.c
index 98b4bb3..f4d8fe6 100644 (file)
--- a/vnc.c
+++ b/vnc.c
 #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(VncState *vs, int feature) {
    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);
 
@@ -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->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);
 
@@ -897,19 +872,20 @@ static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
     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;
@@ -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_expect = expecting;
 }
 
-static void vnc_client_read(void *opaque)
+void vnc_client_read(void *opaque)
 {
     VncState *vs = opaque;
     long ret;
@@ -946,8 +922,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;
@@ -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);
 
@@ -992,12 +968,12 @@ static void vnc_write(VncState *vs, const void *data, size_t 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);
 }
 
-static void vnc_write_u32(VncState *vs, uint32_t value)
+void vnc_write_u32(VncState *vs, uint32_t value)
 {
     uint8_t buf[4];
 
@@ -1009,7 +985,7 @@ static void vnc_write_u32(VncState *vs, uint32_t value)
     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];
 
@@ -1019,74 +995,39 @@ static void vnc_write_u16(VncState *vs, uint16_t value)
     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)
 {
 }
@@ -1669,6 +1610,11 @@ static int protocol_client_init(VncState *vs, uint8_t *data, size_t len)
     return 0;
 }
 
+void start_client_init(VncState *vs)
+{
+    vnc_read_when(vs, protocol_client_init, 1);
+}
+
 static void make_challenge(VncState *vs)
 {
     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_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 */
@@ -1737,411 +1683,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
@@ -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_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 */
@@ -2227,7 +1773,7 @@ static int protocol_version(VncState *vs, uint8_t *version, size_t len)
             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);
@@ -2329,61 +1875,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)
 {
@@ -2403,7 +1894,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
 }
 
@@ -2459,7 +1950,7 @@ int vnc_display_open(DisplayState *ds, const char *display)
            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 */
@@ -2470,7 +1961,7 @@ int vnc_display_open(DisplayState *ds, const char *display)
                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 --git a/vnc.h b/vnc.h
index e7467e1..b5ae1f9 100644 (file)
--- a/vnc.h
+++ b/vnc.h
 #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"
 
+// #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
@@ -73,6 +76,11 @@ typedef void VncSendHextileTile(VncState *vs,
 
 typedef struct VncDisplay VncDisplay;
 
+#ifdef CONFIG_VNC_TLS
+#include "vnc-tls.h"
+#include "vnc-auth-vencrypt.h"
+#endif
+
 struct VncDisplay
 {
     int lsock;
@@ -84,13 +92,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
 };
 
@@ -118,8 +121,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;
@@ -163,12 +165,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,
@@ -179,12 +175,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 */
 
 /*****************************************************************************
  *
@@ -255,4 +245,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 */