Index: Makefile.target =================================================================== RCS file: /sources/qemu/qemu/Makefile.target,v retrieving revision 1.145 diff -u -p -u -p -r1.145 Makefile.target --- Makefile.target 10 Feb 2007 21:52:52 -0000 1.145 +++ Makefile.target 24 Feb 2007 15:45:43 -0000 @@ -193,6 +193,8 @@ endif ifdef CONFIG_SOLARIS LIBS+=-lsocket -lnsl -lresolv endif +LIBS+=$(shell pkg-config --libs gnutls) +CPPFLAGS+=$(shell pkg-config --cflags gnutls) # profiling code ifdef TARGET_GPROF @@ -414,7 +416,7 @@ endif ifdef CONFIG_SDL VL_OBJS+=sdl.o x_keymap.o endif -VL_OBJS+=vnc.o +VL_OBJS+=vnc.o d3des.o ifdef CONFIG_COCOA VL_OBJS+=cocoa.o COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit @@ -472,7 +474,7 @@ cocoa.o: cocoa.m sdl.o: sdl.c keymaps.c sdl_keysym.h $(CC) $(CFLAGS) $(CPPFLAGS) $(SDL_CFLAGS) $(BASE_CFLAGS) -c -o $@ $< -vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h +vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h d3des.h $(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< sdlaudio.o: sdlaudio.c Index: vl.c =================================================================== RCS file: /sources/qemu/qemu/vl.c,v retrieving revision 1.259 diff -u -p -u -p -r1.259 vl.c --- vl.c 22 Feb 2007 01:48:01 -0000 1.259 +++ vl.c 24 Feb 2007 15:45:44 -0000 @@ -4487,8 +4487,7 @@ int qemu_set_fd_handler2(int fd, if (ioh == NULL) break; if (ioh->fd == fd) { - *pioh = ioh->next; - qemu_free(ioh); + ioh->fd = -1; break; } pioh = &ioh->next; @@ -6157,7 +6156,7 @@ void qemu_system_powerdown_request(void) void main_loop_wait(int timeout) { - IOHandlerRecord *ioh, *ioh_next; + IOHandlerRecord *ioh, *ioh_next, *ioh_prev; fd_set rfds, wfds, xfds; int ret, nfds; struct timeval tv; @@ -6219,16 +6218,31 @@ void main_loop_wait(int timeout) #endif ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv); if (ret > 0) { - /* XXX: better handling of removal */ for(ioh = first_io_handler; ioh != NULL; ioh = ioh_next) { ioh_next = ioh->next; if (FD_ISSET(ioh->fd, &rfds)) { ioh->fd_read(ioh->opaque); } - if (FD_ISSET(ioh->fd, &wfds)) { + if (ioh->fd != -1 && FD_ISSET(ioh->fd, &wfds)) { ioh->fd_write(ioh->opaque); } } + ioh_prev = NULL; + ioh = first_io_handler; + while (ioh) { + ioh_next = ioh->next; + if (ioh->fd == -1) { + if (ioh_prev == NULL) { + first_io_handler = ioh_next; + } else { + ioh_prev->next = ioh_next; + } + qemu_free(ioh); + } else { + ioh_prev = ioh; + } + ioh = ioh_next; + } } #if defined(CONFIG_SLIRP) if (slirp_inited) { Index: vnc.c =================================================================== RCS file: /sources/qemu/qemu/vnc.c,v retrieving revision 1.12 diff -u -p -u -p -r1.12 vnc.c --- vnc.c 5 Feb 2007 20:20:30 -0000 1.12 +++ vnc.c 24 Feb 2007 15:45:44 -0000 @@ -30,6 +30,10 @@ #include "vnc_keysym.h" #include "keymaps.c" +#include "d3des.h" + +#include +#include typedef struct Buffer { @@ -54,12 +58,66 @@ typedef void VncSendHextileTile(VncState #define VNC_MAX_HEIGHT 2048 #define VNC_DIRTY_WORDS (VNC_MAX_WIDTH / (16 * 32)) +enum { + VNC_WIREMODE_CLEAR, + VNC_WIREMODE_TLS_HANDSHAKE, + VNC_WIREMODE_TLS_SESSION, +}; + +enum { + VNC_WIRE_AUTH_CLEAR, + VNC_WIRE_AUTH_TLS_ANON, + VNC_WIRE_AUTH_TLS_X509, +}; + +enum { + VNC_AUTH_INVALID = 0, + VNC_AUTH_NONE = 1, + VNC_AUTH_VNC = 2, + VNC_AUTH_RA2 = 5, + VNC_AUTH_RA2NE = 6, + VNC_AUTH_TIGHT = 16, + VNC_AUTH_ULTRA = 17, + VNC_AUTH_TLS = 18, + VNC_AUTH_VENCRYPT = 19 +}; + +enum { + VNC_AUTH_VENCRYPT_PLAIN = 256, + VNC_AUTH_VENCRYPT_TLSNONE = 257, + VNC_AUTH_VENCRYPT_TLSVNC = 258, + VNC_AUTH_VENCRYPT_TLSPLAIN = 259, + VNC_AUTH_VENCRYPT_X509NONE = 260, + VNC_AUTH_VENCRYPT_X509VNC = 261, + VNC_AUTH_VENCRYPT_X509PLAIN = 262, +}; + +#define CA_FILE "ca-cert.pem" +#define CRL_FILE "ca-crl.pem" +#define KEY_FILE "key.pem" +#define CERT_FILE "cert.pem" + +#define MAX_AUTH 20 +#define AUTHCHALLENGESIZE 16 + struct VncState { QEMUTimer *timer; int lsock; int csock; DisplayState *ds; + + int major; + int minor; + + int auth; + int subauth; + + int wiremode; + gnutls_session_t tls_session; + char *password; + char challenge[AUTHCHALLENGESIZE]; + int need_update; int width; int height; @@ -94,6 +152,19 @@ struct VncState static VncState *vnc_state; /* needed for info vnc */ +#if 1 +#define VNC_DEBUG(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) +#else +#define VNC_DEBUG(fmt, ...) do { } while (0) +#endif + +static void debug_log(int level, const char* str) { +#if 0 + VNC_DEBUG("%d %s", level, str); +#endif +} + + void do_info_vnc(void) { if (vnc_state == NULL) @@ -546,16 +617,21 @@ static void buffer_append(Buffer *buffer static int vnc_client_io_error(VncState *vs, int ret, int last_errno) { - if (ret == 0 || ret == -1) { - if (ret == -1 && (last_errno == EINTR || last_errno == EAGAIN)) + if (ret <= 0) { + if (ret < 0 && (last_errno == EINTR || last_errno == EAGAIN)) return 0; + VNC_DEBUG("Closing down client sock\n"); qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL); closesocket(vs->csock); vs->csock = -1; buffer_reset(&vs->input); buffer_reset(&vs->output); vs->need_update = 0; + if (vs->tls_session) { + gnutls_deinit(vs->tls_session); + vs->tls_session = NULL; + } return 0; } return ret; @@ -571,7 +647,10 @@ static void vnc_client_write(void *opaqu long ret; VncState *vs = opaque; - ret = send(vs->csock, vs->output.buffer, vs->output.offset, 0); + if (vs->tls_session) { + ret = gnutls_write(vs->tls_session, vs->output.buffer, vs->output.offset); + } else + ret = send(vs->csock, vs->output.buffer, vs->output.offset, 0); ret = vnc_client_io_error(vs, ret, socket_error()); if (!ret) return; @@ -597,7 +676,10 @@ static void vnc_client_read(void *opaque buffer_reserve(&vs->input, 4096); - ret = recv(vs->csock, buffer_end(&vs->input), 4096, 0); + if (vs->tls_session) + ret = gnutls_read(vs->tls_session, buffer_end(&vs->input), 4096); + else + ret = recv(vs->csock, buffer_end(&vs->input), 4096, 0); ret = vnc_client_io_error(vs, ret, socket_error()); if (!ret) return; @@ -692,6 +774,44 @@ static uint32_t read_u32(uint8_t *data, (data[offset + 2] << 8) | data[offset + 3]); } +ssize_t vnc_tls_push(gnutls_transport_ptr_t transport, + const void *data, + size_t len) { + struct VncState *vs = (struct VncState *)transport; + int ret, lastErrno; + + ret = send(vs->csock, data, len, 0); + lastErrno = socket_error(); + ret = vnc_client_io_error(vs, ret, lastErrno); + errno = lastErrno; + if (!ret) { + VNC_DEBUG("Failed errno %d\n", errno); + return -1; + } + + return ret; +} + + +ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport, + void *data, + size_t len) { + struct VncState *vs = (struct VncState *)transport; + int ret, lastErrno; + + ret = recv(vs->csock, data, len, 0); + lastErrno = socket_error(); + ret = vnc_client_io_error(vs, ret, lastErrno); + errno = lastErrno; + if (!ret) { + VNC_DEBUG("Failed errno %d\n", errno); + return -1; + } + + return ret; +} + + static void client_cut_text(VncState *vs, size_t len, char *text) { } @@ -1109,23 +1229,523 @@ static int protocol_client_init(VncState return 0; } + + +static void make_challenge(VncState *vs) +{ + int i; + + srand(time(NULL)+getpid()+getpid()*987654+rand()); + + for (i = 0 ; i < sizeof(vs->challenge) ; i++) + vs->challenge[i] = (int) (256.0*rand()/(RAND_MAX+1.0)); +} + +static int protocol_client_auth_vnc(VncState *vs, char *data, size_t len) +{ + char response[AUTHCHALLENGESIZE]; + int i, j, pwlen; + char key[8]; + + memcpy(response, vs->challenge, AUTHCHALLENGESIZE); + + /* Calculate the sent challenge */ + pwlen = strlen(vs->password); + for (i=0; ipassword[i] : 0; + deskey(key, EN0); + for (j = 0; j < AUTHCHALLENGESIZE; j += 8) + des(response+j, response+j); + + if (memcmp(response, data, AUTHCHALLENGESIZE) != 0) { + VNC_DEBUG("Client challenge reponse did not match\n"); + vnc_write_u32(vs, 1); /* Reject auth */ + if (vs->minor >= 8) { + static const char err[] = "Authentication failed"; + vnc_write_u32(vs, sizeof(err)); + vnc_write(vs, err, sizeof(err)); + } + vnc_flush(vs); + vnc_client_error(vs); + } else { + VNC_DEBUG("Accepting VNC challenge response\n"); + vnc_write_u32(vs, 0); /* Accept auth */ + vnc_flush(vs); + + vnc_read_when(vs, protocol_client_init, 1); + } + return 0; +} + +static int start_auth_vnc(VncState *vs) +{ + make_challenge(vs); + vnc_write(vs, vs->challenge, sizeof(vs->challenge)); + vnc_flush(vs); + + vnc_read_when(vs, protocol_client_auth_vnc, sizeof(vs->challenge)); + return 0; +} + +#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; + + if (gnutls_dh_params_init (&dh_params) < 0) + return 0; + if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0) + return 0; + + gnutls_global_set_log_level(10); + gnutls_global_set_log_function(debug_log); + + 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_cert_cred(void) +{ + gnutls_certificate_credentials_t x509_cred; + int ret; + struct stat st; + + 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, CA_FILE, GNUTLS_X509_FMT_PEM)) < 0) { + VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret)); + return NULL; + } + + if ((ret = gnutls_certificate_set_x509_key_file (x509_cred, CERT_FILE, KEY_FILE, + GNUTLS_X509_FMT_PEM)) < 0) { + VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret)); + return NULL; + } + + if (stat(CRL_FILE, &st) < 0) { + if (errno != ENOENT) { + return NULL; + } + } else { + if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred, CRL_FILE, GNUTLS_X509_FMT_PEM)) < 0) { + VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret)); + return NULL; + } + } + + gnutls_certificate_set_dh_params (x509_cred, dh_params); + + return x509_cred; +} + +static int vnc_validate_certificate(struct VncState *vnc) +{ + int ret; + unsigned int status; + const gnutls_datum_t *certs; + unsigned int nCerts, i; + time_t now; + + VNC_DEBUG("Validating\n"); + if ((ret = gnutls_certificate_verify_peers2 (vnc->tls_session, &status)) < 0) { + VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret)); + return 0; + } + + if ((now = time(NULL)) == ((time_t)-1)) { + return 0; + } + + 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 0; + } else { + VNC_DEBUG("Certificate is valid!\n"); + } + + if (gnutls_certificate_type_get(vnc->tls_session) != GNUTLS_CRT_X509) + return 0; + + if (!(certs = gnutls_certificate_get_peers(vnc->tls_session, &nCerts))) + return 0; + + for (i = 0 ; i < nCerts ; i++) { + gnutls_x509_crt_t cert; + VNC_DEBUG ("Checking chain %d\n", i); + if (gnutls_x509_crt_init (&cert) < 0) + return 0; + + if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) { + gnutls_x509_crt_deinit (cert); + return 0; + } + + if (gnutls_x509_crt_get_expiration_time (cert) < now) { + VNC_DEBUG("The certificate has expired\n"); + gnutls_x509_crt_deinit (cert); + return 0; + } + + if (gnutls_x509_crt_get_activation_time (cert) > now) { + VNC_DEBUG("The certificate is not yet activated\n"); + gnutls_x509_crt_deinit (cert); + return 0; + } + + if (gnutls_x509_crt_get_activation_time (cert) > now) { + VNC_DEBUG("The certificate is not yet activated\n"); + gnutls_x509_crt_deinit (cert); + return 0; + } + } + + return 1; +} + +static int start_auth_vencrypt_subauth(VncState *vs) +{ + switch (vs->subauth) { + case VNC_AUTH_VENCRYPT_TLSNONE: + case VNC_AUTH_VENCRYPT_X509NONE: + VNC_DEBUG("Accept 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 VNC auth\n"); + return start_auth_vnc(vs); + + default: /* Should not be possible, but just in case */ + VNC_DEBUG("Reject auth %d\n", vs->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 anonTLS) { + int ret; + + if ((ret = gnutls_handshake(vs->tls_session)) < 0) { + if (!gnutls_error_is_fatal(ret)) { + 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); + VNC_DEBUG("Handshake interrupted (blocking)\n"); + return 0; + } + VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret)); + vnc_client_error(vs); + return -1; + } + + VNC_DEBUG("Handshake done\n"); + + if (anonTLS) { + VNC_DEBUG("Anonymous TLS, no certificate checks\n"); + } else { + if (!vnc_validate_certificate(vs)) { + VNC_DEBUG("Certificate validation failed\n"); + vnc_client_error(vs); + return -1; + } + } + + VNC_DEBUG("Switching to TLS data mode\n"); + vs->wiremode = VNC_WIREMODE_TLS_SESSION; + 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; + int anon = 0; + + if (vs->subauth == VNC_AUTH_VENCRYPT_TLSNONE || + vs->subauth == VNC_AUTH_VENCRYPT_TLSVNC || + vs->subauth == VNC_AUTH_VENCRYPT_TLSPLAIN) + anon = 1; + + vnc_continue_handshake(vs, anon); +} + +static int vnc_start_tls(struct VncState *vs, int anonTLS) { + 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_priority[] = {GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0}; + static const int kx_anon[] = {GNUTLS_KX_ANON_DH, 0}; + + VNC_DEBUG("Do TLS handshake, anon ? %d\n", anonTLS); + 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) { + vnc_client_error(vs); + return -1; + } + + if (gnutls_kx_set_priority(vs->tls_session, anonTLS ? kx_anon : kx_priority) < 0) { + vnc_client_error(vs); + return -1; + } + + if (gnutls_certificate_type_set_priority(vs->tls_session, cert_type_priority) < 0) { + vnc_client_error(vs); + return -1; + } + + if (gnutls_protocol_set_priority(vs->tls_session, protocol_priority) < 0) { + vnc_client_error(vs); + return -1; + } + + if (anonTLS) { + gnutls_anon_server_credentials anon_cred = vnc_tls_initialize_anon_cred(); + if (!anon_cred) { + vnc_client_error(vs); + return -1; + } + if (gnutls_credentials_set(vs->tls_session, GNUTLS_CRD_ANON, anon_cred) < 0) { + vnc_client_error(vs); + return -1; + } + } else { + gnutls_certificate_credentials_t x509_cred = vnc_tls_initialize_cert_cred(); + if (!x509_cred) { + vnc_client_error(vs); + return -1; + } + if (gnutls_credentials_set(vs->tls_session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) { + vnc_client_error(vs); + return -1; + } + gnutls_certificate_server_set_request(vs->tls_session, GNUTLS_CERT_REQUEST); + } + + 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 vnc_continue_handshake(vs, anonTLS); +} + + +static int protocol_client_vencrypt_auth(VncState *vs, char *data, size_t len) +{ + int auth = read_u32(data, 0); + + if (auth != vs->subauth) { + VNC_DEBUG("Rejecting auth %d\n", auth); + vnc_write_u8(vs, 0); /* Reject auth */ + vnc_flush(vs); + vnc_client_error(vs); + } else { + int anon = 0; + VNC_DEBUG("Accepting auth %d, starting handshake\n", auth); + vnc_write_u8(vs, 1); /* Accept auth */ + vnc_flush(vs); + + if (vs->subauth == VNC_AUTH_VENCRYPT_TLSNONE || + vs->subauth == VNC_AUTH_VENCRYPT_TLSVNC || + vs->subauth == VNC_AUTH_VENCRYPT_TLSPLAIN) + anon = 1; + + if (vnc_start_tls(vs, anon) < 0) { + VNC_DEBUG("Failed to complete TLS\n"); + return 0; + } + + if (vs->wiremode == VNC_WIREMODE_TLS_SESSION) { + VNC_DEBUG("Starting VeNCrypt subauth\n"); + return start_auth_vencrypt_subauth(vs); + } else { + VNC_DEBUG("TLS handshake blocked\n"); + return 0; + } + } + return 0; +} + +static int protocol_client_vencrypt_init(VncState *vs, char *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->subauth); + vnc_write_u8(vs, 0); /* Accept version */ + vnc_write_u8(vs, 1); /* Number of sub-auths */ + vnc_write_u32(vs, vs->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; +} + +static int protocol_client_auth(VncState *vs, char *data, size_t len) +{ + if (data[0] != vs->auth) { /* Reject auth */ + VNC_DEBUG("Reject auth %d\n", (int)data[0]); + vnc_write_u32(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); + } else { /* Accept requested auth */ + VNC_DEBUG("Client requested auth %d\n", (int)data[0]); + switch (vs->auth) { + case VNC_AUTH_NONE: + VNC_DEBUG("Accept auth none\n"); + vnc_write_u32(vs, 0); /* Accept auth completion */ + vnc_read_when(vs, protocol_client_init, 1); + break; + + case VNC_AUTH_VNC: + VNC_DEBUG("Start VNC auth\n"); + return start_auth_vnc(vs); + + case VNC_AUTH_VENCRYPT: + VNC_DEBUG("Start VeNCrypt auth %d\n", vs->auth); + return start_auth_vencrypt(vs); + + default: /* Should not be possible, but just in case */ + VNC_DEBUG("Reject auth %d\n", vs->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 int protocol_version(VncState *vs, char *version, size_t len) { char local[13]; - int maj, min; memcpy(local, version, 12); local[12] = 0; - if (sscanf(local, "RFB %03d.%03d\n", &maj, &min) != 2) { - vnc_client_error(vs); - return 0; + if (sscanf(local, "RFB %03d.%03d\n", &vs->major, &vs->minor) != 2) { + VNC_DEBUG("Malformed version response\n"); + vnc_client_error(vs); + return 0; } - vnc_write_u32(vs, 1); /* None */ - vnc_flush(vs); + if (vs->major != 3 || + (vs->minor != 3 && + vs->minor != 7 && + vs->minor != 8)) { + VNC_DEBUG("Unsupported version %d.%d\n", vs->major, vs->minor); + vnc_write_u32(vs, VNC_AUTH_INVALID); + vnc_flush(vs); + vnc_client_error(vs); + return 0; + } - vnc_read_when(vs, protocol_client_init, 1); + VNC_DEBUG("Using protocol %d.%d\n", vs->major, vs->minor); + + if (vs->minor == 3) { + if (vs->auth == VNC_AUTH_NONE) { + VNC_DEBUG("Accept auth none\n"); + vnc_write_u32(vs, vs->auth); + vnc_flush(vs); + vnc_read_when(vs, protocol_client_init, 1); + } else if (vs->auth == VNC_AUTH_VNC) { + VNC_DEBUG("Start VNC auth\n"); + vnc_write_u32(vs, vs->auth); + vnc_flush(vs); + start_auth_vnc(vs); + } else { + VNC_DEBUG("Unsupported auth %d in protocol 3.3\n", vs->auth); + vnc_write_u32(vs, VNC_AUTH_INVALID); + vnc_flush(vs); + vnc_client_error(vs); + } + } else { + VNC_DEBUG("Telling client we support auth %d\n", vs->auth); + vnc_write_u8(vs, 1); /* num auth */ + vnc_write_u8(vs, vs->auth); + vnc_read_when(vs, protocol_client_auth, 1); + vnc_flush(vs); + } return 0; } @@ -1139,20 +1759,60 @@ static void vnc_listen_read(void *opaque vs->csock = accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); if (vs->csock != -1) { socket_set_nonblock(vs->csock); - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, opaque); - vnc_write(vs, "RFB 003.003\n", 12); - vnc_flush(vs); - vnc_read_when(vs, protocol_version, 12); - memset(vs->old_data, 0, vs->ds->linesize * vs->ds->height); - memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row)); - vs->has_resize = 0; - vs->has_hextile = 0; - vs->ds->dpy_copy = NULL; + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, opaque); + vnc_write(vs, "RFB 003.008\n", 12); + vnc_flush(vs); + vnc_read_when(vs, protocol_version, 12); + memset(vs->old_data, 0, vs->ds->linesize * vs->ds->height); + memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row)); + vs->has_resize = 0; + vs->has_hextile = 0; + vs->ds->dpy_copy = NULL; + vs->wiremode = VNC_WIREMODE_CLEAR; + vs->tls_session = NULL; } } extern int parse_host_port(struct sockaddr_in *saddr, const char *str); +void vnc_init_auth(VncState *vs, int mode, const char *password) +{ + if (vs->password) + free(vs->password); + if (password) + vs->password = strdup(password); + else + vs->password = NULL; + + switch (mode) { + default: + case VNC_WIRE_AUTH_CLEAR: + if (vs->password) + vs->auth = VNC_AUTH_VNC; + else + vs->auth = VNC_AUTH_NONE; + break; + + case VNC_WIRE_AUTH_TLS_ANON: + vs->auth = VNC_AUTH_VENCRYPT; + if (vs->password) + vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC; + else + vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE; + break; + + case VNC_WIRE_AUTH_TLS_X509: + vs->auth = VNC_AUTH_VENCRYPT; + if (vs->password) + vs->subauth = VNC_AUTH_VENCRYPT_X509VNC; + else + vs->subauth = VNC_AUTH_VENCRYPT_X509NONE; + break; + } + + VNC_DEBUG("Selected auth mode %d password '%s'\n", vs->auth, vs->password); +} + void vnc_display_init(DisplayState *ds, const char *arg) { struct sockaddr *addr; @@ -1173,6 +1833,12 @@ void vnc_display_init(DisplayState *ds, vnc_state = vs; vs->display = arg; + /* XXX Settable from command line or monitor */ + //vnc_init_auth(vs, VNC_WIRE_AUTH_CLEAR, NULL); + //vnc_init_auth(vs, VNC_WIRE_AUTH_CLEAR, "123456"); + vnc_init_auth(vs, VNC_WIRE_AUTH_TLS_ANON, "123456"); + //vnc_init_auth(vs, VNC_WIRE_AUTH_TLS_X509, "123456"); + vs->lsock = -1; vs->csock = -1; vs->depth = 4; @@ -1256,3 +1922,11 @@ void vnc_display_init(DisplayState *ds, exit(1); } } + +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 8 + * End: + */