diff --git a/.cvsignore b/.cvsignore
index c2d6b91..53c87a3 100644
--- a/.cvsignore
+++ b/.cvsignore
@@ -1 +1 @@
-qemu-kvm-devel-85rc1.git-snapshot-20090323.tar.gz
+qemu-kvm-0.10.tar.gz
diff --git a/01-tls-handshake-fix.patch b/01-tls-handshake-fix.patch
new file mode 100644
index 0000000..112c4fe
--- /dev/null
+++ b/01-tls-handshake-fix.patch
@@ -0,0 +1,19 @@
+Index: qemu-kvm-0.10/qemu/vnc.c
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/vnc.c
++++ qemu-kvm-0.10/qemu/vnc.c
+@@ -2096,14 +2096,6 @@ static int protocol_client_vencrypt_auth
+ 	    VNC_DEBUG("Failed to complete TLS\n");
+ 	    return 0;
+ 	}
+-
+-	if (vs->wiremode == VNC_WIREMODE_TLS) {
+-	    VNC_DEBUG("Starting VeNCrypt subauth\n");
+-	    return start_auth_vencrypt_subauth(vs);
+-	} else {
+-	    VNC_DEBUG("TLS handshake blocked\n");
+-	    return 0;
+-	}
+     }
+     return 0;
+ }
diff --git a/02-vnc-monitor-info.patch b/02-vnc-monitor-info.patch
new file mode 100644
index 0000000..04a3988
--- /dev/null
+++ b/02-vnc-monitor-info.patch
@@ -0,0 +1,152 @@
+Index: qemu-kvm-0.10/qemu/vnc.c
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/vnc.c
++++ qemu-kvm-0.10/qemu/vnc.c
+@@ -166,19 +166,136 @@ struct VncState
+ static VncDisplay *vnc_display; /* needed for info vnc */
+ static DisplayChangeListener *dcl;
+ 
++static char *addr_to_string(const char *format,
++			    struct sockaddr_storage *sa,
++			    socklen_t salen) {
++    char *addr;
++    char host[NI_MAXHOST];
++    char serv[NI_MAXSERV];
++    int err;
++
++    if ((err = getnameinfo((struct sockaddr *)sa, salen,
++			   host, sizeof(host),
++			   serv, sizeof(serv),
++			   NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
++	VNC_DEBUG("Cannot resolve address %d: %s\n",
++		  err, gai_strerror(err));
++	return NULL;
++    }
++
++    if (asprintf(&addr, format, host, serv) < 0)
++	return NULL;
++
++    return addr;
++}
++
++static char *vnc_socket_local_addr(const char *format, int fd) {
++    struct sockaddr_storage sa;
++    socklen_t salen;
++
++    salen = sizeof(sa);
++    if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0)
++	return NULL;
++
++    return addr_to_string(format, &sa, salen);
++}
++
++static char *vnc_socket_remote_addr(const char *format, int fd) {
++    struct sockaddr_storage sa;
++    socklen_t salen;
++
++    salen = sizeof(sa);
++    if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0)
++	return NULL;
++
++    return addr_to_string(format, &sa, salen);
++}
++
++static const char *vnc_auth_name(VncDisplay *vd) {
++    switch (vd->auth) {
++    case VNC_AUTH_INVALID:
++	return "invalid";
++    case VNC_AUTH_NONE:
++	return "none";
++    case VNC_AUTH_VNC:
++	return "vnc";
++    case VNC_AUTH_RA2:
++	return "ra2";
++    case VNC_AUTH_RA2NE:
++	return "ra2ne";
++    case VNC_AUTH_TIGHT:
++	return "tight";
++    case VNC_AUTH_ULTRA:
++	return "ultra";
++    case VNC_AUTH_TLS:
++	return "tls";
++    case VNC_AUTH_VENCRYPT:
++#ifdef CONFIG_VNC_TLS
++	switch (vd->subauth) {
++	case VNC_AUTH_VENCRYPT_PLAIN:
++	    return "vencrypt+plain";
++	case VNC_AUTH_VENCRYPT_TLSNONE:
++	    return "vencrypt+tls+none";
++	case VNC_AUTH_VENCRYPT_TLSVNC:
++	    return "vencrypt+tls+vnc";
++	case VNC_AUTH_VENCRYPT_TLSPLAIN:
++	    return "vencrypt+tls+plain";
++	case VNC_AUTH_VENCRYPT_X509NONE:
++	    return "vencrypt+x509+none";
++	case VNC_AUTH_VENCRYPT_X509VNC:
++	    return "vencrypt+x509+vnc";
++	case VNC_AUTH_VENCRYPT_X509PLAIN:
++	    return "vencrypt+x509+plain";
++	default:
++	    return "vencrypt";
++	}
++#else
++	return "vencrypt";
++#endif
++    }
++    return "unknown";
++}
++
++#define VNC_SOCKET_FORMAT_PRETTY "local %s:%s"
++
++static void do_info_vnc_client(VncState *client)
++{
++    char *clientAddr =
++	vnc_socket_remote_addr("     address: %s:%s\n",
++			       client->csock);
++    if (!clientAddr)
++	return;
++
++    term_puts("Client:\n");
++    term_puts(clientAddr);
++    free(clientAddr);
++}
++
+ void do_info_vnc(void)
+ {
+-    if (vnc_display == NULL || vnc_display->display == NULL)
+-	term_printf("VNC server disabled\n");
+-    else {
+-	term_printf("VNC server active on: ");
+-	term_print_filename(vnc_display->display);
+-	term_printf("\n");
+-
+-	if (vnc_display->clients == NULL)
+-	    term_printf("No client connected\n");
+-	else
+-	    term_printf("Client connected\n");
++    if (vnc_display == NULL || vnc_display->display == NULL) {
++	term_printf("Server: disabled\n");
++    } else {
++	char *serverAddr = vnc_socket_local_addr("     address: %s:%s\n",
++						 vnc_display->lsock);
++
++	if (!serverAddr)
++	    return;
++
++	term_puts("Server:\n");
++	term_puts(serverAddr);
++	free(serverAddr);
++	term_printf("        auth: %s\n", vnc_auth_name(vnc_display));
++
++	if (vnc_display->clients) {
++	    VncState *client = vnc_display->clients;
++	    while (client) {
++		do_info_vnc_client(client);
++		client = client->next;
++	    }
++	} else {
++	    term_printf("Client: none\n");
++	}
+     }
+ }
+ 
diff --git a/03-display-keymaps.patch b/03-display-keymaps.patch
new file mode 100644
index 0000000..6819d74
--- /dev/null
+++ b/03-display-keymaps.patch
@@ -0,0 +1,330 @@
+Index: qemu-kvm-0.10/qemu/Makefile
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/Makefile
++++ qemu-kvm-0.10/qemu/Makefile
+@@ -141,6 +141,7 @@ endif
+ AUDIO_OBJS+= wavcapture.o
+ OBJS+=$(addprefix audio/, $(AUDIO_OBJS))
+ 
++OBJS+=keymaps.o
+ ifdef CONFIG_SDL
+ OBJS+=sdl.o x_keymap.o
+ endif
+@@ -165,15 +166,17 @@ LIBS+=$(VDE_LIBS)
+ 
+ cocoa.o: cocoa.m
+ 
+-sdl.o: sdl.c keymaps.c sdl_keysym.h
++keymaps.o: keymaps.c keymaps.h
++
++sdl.o: sdl.c keymaps.h sdl_keysym.h
+ 
+ sdl.o audio/sdlaudio.o: CFLAGS += $(SDL_CFLAGS)
+ 
+-vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h d3des.c d3des.h
++vnc.o: vnc.c keymaps.h sdl_keysym.h vnchextile.h d3des.c d3des.h
+ 
+ vnc.o: CFLAGS += $(CONFIG_VNC_TLS_CFLAGS)
+ 
+-curses.o: curses.c keymaps.c curses_keys.h
++curses.o: curses.c keymaps.h curses_keys.h
+ 
+ bt-host.o: CFLAGS += $(CONFIG_BLUEZ_CFLAGS)
+ 
+Index: qemu-kvm-0.10/qemu/curses.c
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/curses.c
++++ qemu-kvm-0.10/qemu/curses.c
+@@ -158,7 +158,6 @@ static void curses_cursor_position(Displ
+ /* generic keyboard conversion */
+ 
+ #include "curses_keys.h"
+-#include "keymaps.c"
+ 
+ static kbd_layout_t *kbd_layout = 0;
+ static int keycode2keysym[CURSES_KEYS];
+@@ -311,7 +310,7 @@ static void curses_keyboard_setup(void)
+         keyboard_layout = "en-us";
+ #endif
+     if(keyboard_layout) {
+-        kbd_layout = init_keyboard_layout(keyboard_layout);
++        kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
+         if (!kbd_layout)
+             exit(1);
+     }
+Index: qemu-kvm-0.10/qemu/curses_keys.h
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/curses_keys.h
++++ qemu-kvm-0.10/qemu/curses_keys.h
+@@ -21,6 +21,10 @@
+  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  * THE SOFTWARE.
+  */
++
++#include "keymaps.h"
++
++
+ #define KEY_RELEASE         0x80
+ #define KEY_MASK            0x7f
+ #define SHIFT_CODE          0x2a
+@@ -239,11 +243,6 @@ static const int curses2keysym[CURSES_KE
+ 
+ };
+ 
+-typedef struct {
+-	const char* name;
+-	int keysym;
+-} name2keysym_t;
+-
+ static const name2keysym_t name2keysym[] = {
+     /* Plain ASCII */
+     { "space", 0x020 },
+Index: qemu-kvm-0.10/qemu/keymaps.c
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/keymaps.c
++++ qemu-kvm-0.10/qemu/keymaps.c
+@@ -22,34 +22,20 @@
+  * THE SOFTWARE.
+  */
+ 
+-static int get_keysym(const char *name)
++#include "keymaps.h"
++#include "sysemu.h"
++
++static int get_keysym(const name2keysym_t *table,
++		      const char *name)
+ {
+     const name2keysym_t *p;
+-    for(p = name2keysym; p->name != NULL; p++) {
++    for(p = table; p->name != NULL; p++) {
+         if (!strcmp(p->name, name))
+             return p->keysym;
+     }
+     return 0;
+ }
+ 
+-struct key_range {
+-    int start;
+-    int end;
+-    struct key_range *next;
+-};
+-
+-#define MAX_NORMAL_KEYCODE 512
+-#define MAX_EXTRA_COUNT 256
+-typedef struct {
+-    uint16_t keysym2keycode[MAX_NORMAL_KEYCODE];
+-    struct {
+-	int keysym;
+-	uint16_t keycode;
+-    } keysym2keycode_extra[MAX_EXTRA_COUNT];
+-    int extra_count;
+-    struct key_range *keypad_range;
+-    struct key_range *numlock_range;
+-} kbd_layout_t;
+ 
+ static void add_to_key_range(struct key_range **krp, int code) {
+     struct key_range *kr;
+@@ -73,7 +59,8 @@ static void add_to_key_range(struct key_
+     }
+ }
+ 
+-static kbd_layout_t *parse_keyboard_layout(const char *language,
++static kbd_layout_t *parse_keyboard_layout(const name2keysym_t *table,
++					   const char *language,
+ 					   kbd_layout_t * k)
+ {
+     FILE *f;
+@@ -102,7 +89,7 @@ static kbd_layout_t *parse_keyboard_layo
+ 	if (!strncmp(line, "map ", 4))
+ 	    continue;
+ 	if (!strncmp(line, "include ", 8)) {
+-	    parse_keyboard_layout(line + 8, k);
++	    parse_keyboard_layout(table, line + 8, k);
+         } else {
+ 	    char *end_of_keysym = line;
+ 	    while (*end_of_keysym != 0 && *end_of_keysym != ' ')
+@@ -110,7 +97,7 @@ static kbd_layout_t *parse_keyboard_layo
+ 	    if (*end_of_keysym) {
+ 		int keysym;
+ 		*end_of_keysym = 0;
+-		keysym = get_keysym(line);
++		keysym = get_keysym(table, line);
+ 		if (keysym == 0) {
+                     //		    fprintf(stderr, "Warning: unknown keysym %s\n", line);
+ 		} else {
+@@ -154,12 +141,14 @@ static kbd_layout_t *parse_keyboard_layo
+     return k;
+ }
+ 
+-static void *init_keyboard_layout(const char *language)
++
++void *init_keyboard_layout(const name2keysym_t *table, const char *language)
+ {
+-    return parse_keyboard_layout(language, 0);
++    return parse_keyboard_layout(table, language, 0);
+ }
+ 
+-static int keysym2scancode(void *kbd_layout, int keysym)
++
++int keysym2scancode(void *kbd_layout, int keysym)
+ {
+     kbd_layout_t *k = kbd_layout;
+     if (keysym < MAX_NORMAL_KEYCODE) {
+@@ -180,7 +169,7 @@ static int keysym2scancode(void *kbd_lay
+     return 0;
+ }
+ 
+-static inline int keycode_is_keypad(void *kbd_layout, int keycode)
++int keycode_is_keypad(void *kbd_layout, int keycode)
+ {
+     kbd_layout_t *k = kbd_layout;
+     struct key_range *kr;
+@@ -191,7 +180,7 @@ static inline int keycode_is_keypad(void
+     return 0;
+ }
+ 
+-static inline int keysym_is_numlock(void *kbd_layout, int keysym)
++int keysym_is_numlock(void *kbd_layout, int keysym)
+ {
+     kbd_layout_t *k = kbd_layout;
+     struct key_range *kr;
+Index: qemu-kvm-0.10/qemu/keymaps.h
+===================================================================
+--- /dev/null
++++ qemu-kvm-0.10/qemu/keymaps.h
+@@ -0,0 +1,60 @@
++/*
++ * QEMU keysym to keycode conversion using rdesktop keymaps
++ *
++ * Copyright (c) 2004 Johannes Schindelin
++ *
++ * 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_KEYMAPS_H__
++#define __QEMU_KEYMAPS_H__
++
++#include "qemu-common.h"
++
++typedef struct {
++	const char* name;
++	int keysym;
++} name2keysym_t;
++
++struct key_range {
++    int start;
++    int end;
++    struct key_range *next;
++};
++
++#define MAX_NORMAL_KEYCODE 512
++#define MAX_EXTRA_COUNT 256
++typedef struct {
++    uint16_t keysym2keycode[MAX_NORMAL_KEYCODE];
++    struct {
++	int keysym;
++	uint16_t keycode;
++    } keysym2keycode_extra[MAX_EXTRA_COUNT];
++    int extra_count;
++    struct key_range *keypad_range;
++    struct key_range *numlock_range;
++} kbd_layout_t;
++
++
++void *init_keyboard_layout(const name2keysym_t *table, const char *language);
++int keysym2scancode(void *kbd_layout, int keysym);
++int keycode_is_keypad(void *kbd_layout, int keycode);
++int keysym_is_numlock(void *kbd_layout, int keysym);
++
++#endif /* __QEMU_KEYMAPS_H__ */
+Index: qemu-kvm-0.10/qemu/sdl.c
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/sdl.c
++++ qemu-kvm-0.10/qemu/sdl.c
+@@ -109,7 +109,6 @@ static void sdl_resize(DisplayState *ds)
+ /* generic keyboard conversion */
+ 
+ #include "sdl_keysym.h"
+-#include "keymaps.c"
+ 
+ static kbd_layout_t *kbd_layout = NULL;
+ 
+@@ -677,7 +676,7 @@ void sdl_display_init(DisplayState *ds, 
+         keyboard_layout = "en-us";
+ #endif
+     if(keyboard_layout) {
+-        kbd_layout = init_keyboard_layout(keyboard_layout);
++        kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
+         if (!kbd_layout)
+             exit(1);
+     }
+Index: qemu-kvm-0.10/qemu/sdl_keysym.h
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/sdl_keysym.h
++++ qemu-kvm-0.10/qemu/sdl_keysym.h
+@@ -1,7 +1,6 @@
+-typedef struct {
+-	const char* name;
+-	int keysym;
+-} name2keysym_t;
++
++#include "keymaps.h"
++
+ static const name2keysym_t name2keysym[]={
+ /* ascii */
+     { "space",                0x020},
+Index: qemu-kvm-0.10/qemu/vnc.c
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/vnc.c
++++ qemu-kvm-0.10/qemu/vnc.c
+@@ -35,7 +35,6 @@
+ 
+ #include "vnc.h"
+ #include "vnc_keysym.h"
+-#include "keymaps.c"
+ #include "d3des.h"
+ 
+ #ifdef CONFIG_VNC_TLS
+@@ -2420,9 +2419,9 @@ void vnc_display_init(DisplayState *ds)
+     vs->ds = ds;
+ 
+     if (keyboard_layout)
+-        vs->kbd_layout = init_keyboard_layout(keyboard_layout);
++        vs->kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
+     else
+-        vs->kbd_layout = init_keyboard_layout("en-us");
++        vs->kbd_layout = init_keyboard_layout(name2keysym, "en-us");
+ 
+     if (!vs->kbd_layout)
+ 	exit(1);
+Index: qemu-kvm-0.10/qemu/vnc_keysym.h
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/vnc_keysym.h
++++ qemu-kvm-0.10/qemu/vnc_keysym.h
+@@ -1,7 +1,6 @@
+-typedef struct {
+-	const char* name;
+-	int keysym;
+-} name2keysym_t;
++
++#include "keymaps.h"
++
+ static const name2keysym_t name2keysym[]={
+ /* ascii */
+     { "space",                0x020},
diff --git a/04-vnc-struct.patch b/04-vnc-struct.patch
new file mode 100644
index 0000000..eb7a346
--- /dev/null
+++ b/04-vnc-struct.patch
@@ -0,0 +1,304 @@
+Index: qemu-kvm-0.10/qemu/vnc.c
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/vnc.c
++++ qemu-kvm-0.10/qemu/vnc.c
+@@ -3,6 +3,7 @@
+  *
+  * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+  * Copyright (C) 2006 Fabrice Bellard
++ * Copyright (C) 2009 Red Hat, Inc
+  *
+  * Permission is hereby granted, free of charge, to any person obtaining a copy
+  * of this software and associated documentation files (the "Software"), to deal
+@@ -23,25 +24,16 @@
+  * THE SOFTWARE.
+  */
+ 
+-#include "qemu-common.h"
+-#include "console.h"
++#include "vnc.h"
+ #include "sysemu.h"
+ #include "qemu_socket.h"
+ #include "qemu-timer.h"
+-#include "audio/audio.h"
+-#include <zlib.h>
+ 
+ #define VNC_REFRESH_INTERVAL (1000 / 30)
+ 
+-#include "vnc.h"
+ #include "vnc_keysym.h"
+ #include "d3des.h"
+ 
+-#ifdef CONFIG_VNC_TLS
+-#include <gnutls/gnutls.h>
+-#include <gnutls/x509.h>
+-#endif /* CONFIG_VNC_TLS */
+-
+ // #define _VNC_DEBUG 1
+ 
+ #ifdef _VNC_DEBUG
+@@ -64,103 +56,6 @@ static void vnc_debug_gnutls_log(int lev
+     } \
+ }
+ 
+-typedef struct Buffer
+-{
+-    size_t capacity;
+-    size_t offset;
+-    uint8_t *buffer;
+-} Buffer;
+-
+-typedef struct VncState VncState;
+-
+-typedef int VncReadEvent(VncState *vs, uint8_t *data, size_t len);
+-
+-typedef void VncWritePixels(VncState *vs, void *data, int size);
+-
+-typedef void VncSendHextileTile(VncState *vs,
+-                                int x, int y, int w, int h,
+-                                void *last_bg,
+-                                void *last_fg,
+-                                int *has_bg, int *has_fg);
+-
+-#define VNC_MAX_WIDTH 2048
+-#define VNC_MAX_HEIGHT 2048
+-#define VNC_DIRTY_WORDS (VNC_MAX_WIDTH / (16 * 32))
+-
+-#define VNC_AUTH_CHALLENGE_SIZE 16
+-
+-typedef struct VncDisplay VncDisplay;
+-
+-struct VncDisplay
+-{
+-    int lsock;
+-    DisplayState *ds;
+-    VncState *clients;
+-    kbd_layout_t *kbd_layout;
+-
+-    char *display;
+-    char *password;
+-    int auth;
+-#ifdef CONFIG_VNC_TLS
+-    int subauth;
+-    int x509verify;
+-
+-    char *x509cacert;
+-    char *x509cacrl;
+-    char *x509cert;
+-    char *x509key;
+-#endif
+-};
+-
+-struct VncState
+-{
+-    QEMUTimer *timer;
+-    int csock;
+-    DisplayState *ds;
+-    VncDisplay *vd;
+-    int need_update;
+-    uint32_t dirty_row[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS];
+-    char *old_data;
+-    uint32_t features;
+-    int absolute;
+-    int last_x;
+-    int last_y;
+-
+-    uint32_t vnc_encoding;
+-    uint8_t tight_quality;
+-    uint8_t tight_compression;
+-
+-    int major;
+-    int minor;
+-
+-    char challenge[VNC_AUTH_CHALLENGE_SIZE];
+-
+-#ifdef CONFIG_VNC_TLS
+-    int wiremode;
+-    gnutls_session_t tls_session;
+-#endif
+-
+-    Buffer output;
+-    Buffer input;
+-    /* current output mode information */
+-    VncWritePixels *write_pixels;
+-    VncSendHextileTile *send_hextile_tile;
+-    DisplaySurface clientds, serverds;
+-
+-    CaptureVoiceOut *audio_cap;
+-    struct audsettings as;
+-
+-    VncReadEvent *read_handler;
+-    size_t read_handler_expect;
+-    /* input */
+-    uint8_t modifiers_state[256];
+-
+-    Buffer zlib;
+-    Buffer zlib_tmp;
+-    z_stream zlib_stream[4];
+-
+-    VncState *next;
+-};
+ 
+ static VncDisplay *vnc_display; /* needed for info vnc */
+ static DisplayChangeListener *dcl;
+Index: qemu-kvm-0.10/qemu/vnc.h
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/vnc.h
++++ qemu-kvm-0.10/qemu/vnc.h
+@@ -1,5 +1,148 @@
+-#ifndef __VNCTIGHT_H
+-#define __VNCTIGHT_H
++/*
++ * QEMU VNC display driver
++ *
++ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
++ * Copyright (C) 2006 Fabrice Bellard
++ * Copyright (C) 2009 Red Hat, Inc
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++#ifndef __QEMU_VNC_H
++#define __QEMU_VNC_H
++
++#include "qemu-common.h"
++#include "console.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"
++
++/*****************************************************************************
++ *
++ * Core data structures
++ *
++ *****************************************************************************/
++
++typedef struct Buffer
++{
++    size_t capacity;
++    size_t offset;
++    uint8_t *buffer;
++} Buffer;
++
++typedef struct VncState VncState;
++
++typedef int VncReadEvent(VncState *vs, uint8_t *data, size_t len);
++
++typedef void VncWritePixels(VncState *vs, void *data, int size);
++
++typedef void VncSendHextileTile(VncState *vs,
++                                int x, int y, int w, int h,
++                                void *last_bg,
++                                void *last_fg,
++                                int *has_bg, int *has_fg);
++
++#define VNC_MAX_WIDTH 2048
++#define VNC_MAX_HEIGHT 2048
++#define VNC_DIRTY_WORDS (VNC_MAX_WIDTH / (16 * 32))
++
++#define VNC_AUTH_CHALLENGE_SIZE 16
++
++typedef struct VncDisplay VncDisplay;
++
++struct VncDisplay
++{
++    int lsock;
++    DisplayState *ds;
++    VncState *clients;
++    kbd_layout_t *kbd_layout;
++
++    char *display;
++    char *password;
++    int auth;
++#ifdef CONFIG_VNC_TLS
++    int subauth;
++    int x509verify;
++
++    char *x509cacert;
++    char *x509cacrl;
++    char *x509cert;
++    char *x509key;
++#endif
++};
++
++struct VncState
++{
++    QEMUTimer *timer;
++    int csock;
++    DisplayState *ds;
++    VncDisplay *vd;
++    int need_update;
++    uint32_t dirty_row[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS];
++    char *old_data;
++    uint32_t features;
++    int absolute;
++    int last_x;
++    int last_y;
++
++    uint32_t vnc_encoding;
++    uint8_t tight_quality;
++    uint8_t tight_compression;
++
++    int major;
++    int minor;
++
++    char challenge[VNC_AUTH_CHALLENGE_SIZE];
++
++#ifdef CONFIG_VNC_TLS
++    int wiremode;
++    gnutls_session_t tls_session;
++#endif
++
++    Buffer output;
++    Buffer input;
++    /* current output mode information */
++    VncWritePixels *write_pixels;
++    VncSendHextileTile *send_hextile_tile;
++    DisplaySurface clientds, serverds;
++
++    CaptureVoiceOut *audio_cap;
++    struct audsettings as;
++
++    VncReadEvent *read_handler;
++    size_t read_handler_expect;
++    /* input */
++    uint8_t modifiers_state[256];
++
++    Buffer zlib;
++    Buffer zlib_tmp;
++    z_stream zlib_stream[4];
++
++    VncState *next;
++};
++
+ 
+ /*****************************************************************************
+  *
+@@ -111,4 +254,4 @@ enum {
+ #define VNC_FEATURE_ZLIB_MASK                (1 << VNC_FEATURE_ZLIB)
+ #define VNC_FEATURE_COPYRECT_MASK            (1 << VNC_FEATURE_COPYRECT)
+ 
+-#endif /* __VNCTIGHT_H */
++#endif /* __QEMU_VNC_H */
diff --git a/05-vnc-tls-vencrypt.patch b/05-vnc-tls-vencrypt.patch
new file mode 100644
index 0000000..625a5b6
--- /dev/null
+++ b/05-vnc-tls-vencrypt.patch
@@ -0,0 +1,1644 @@
+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 <anthony@codemonkey.ws>
++ * Copyright (C) 2006 Fabrice Bellard
++ * Copyright (C) 2009 Red Hat, Inc
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++#include "vnc.h"
++
++
++static void start_auth_vencrypt_subauth(VncState *vs)
++{
++    switch (vs->vd->subauth) {
++    case VNC_AUTH_VENCRYPT_TLSNONE:
++    case VNC_AUTH_VENCRYPT_X509NONE:
++       VNC_DEBUG("Accept TLS auth none\n");
++       vnc_write_u32(vs, 0); /* Accept auth completion */
++       start_client_init(vs);
++       break;
++
++    case VNC_AUTH_VENCRYPT_TLSVNC:
++    case VNC_AUTH_VENCRYPT_X509VNC:
++       VNC_DEBUG("Start TLS auth VNC\n");
++       start_auth_vnc(vs);
++       break;
++
++    default: /* Should not be possible, but just in case */
++       VNC_DEBUG("Reject auth %d\n", vs->vd->auth);
++       vnc_write_u8(vs, 1);
++       if (vs->minor >= 8) {
++           static const char err[] = "Unsupported authentication type";
++           vnc_write_u32(vs, sizeof(err));
++           vnc_write(vs, err, sizeof(err));
++       }
++       vnc_client_error(vs);
++    }
++}
++
++static void vnc_tls_handshake_io(void *opaque);
++
++static int vnc_start_vencrypt_handshake(struct VncState *vs) {
++    int ret;
++
++    if ((ret = gnutls_handshake(vs->tls.session)) < 0) {
++       if (!gnutls_error_is_fatal(ret)) {
++           VNC_DEBUG("Handshake interrupted (blocking)\n");
++           if (!gnutls_record_get_direction(vs->tls.session))
++               qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs);
++           else
++               qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs);
++           return 0;
++       }
++       VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
++       vnc_client_error(vs);
++       return -1;
++    }
++
++    if (vs->vd->tls.x509verify) {
++	if (vnc_tls_validate_certificate(vs) < 0) {
++	    VNC_DEBUG("Client verification failed\n");
++	    vnc_client_error(vs);
++	    return -1;
++	} else {
++	    VNC_DEBUG("Client verification passed\n");
++	}
++    }
++
++    VNC_DEBUG("Handshake done, switching to TLS data mode\n");
++    vs->tls.wiremode = VNC_WIREMODE_TLS;
++    qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
++
++    start_auth_vencrypt_subauth(vs);
++
++    return 0;
++}
++
++static void vnc_tls_handshake_io(void *opaque) {
++    struct VncState *vs = (struct VncState *)opaque;
++
++    VNC_DEBUG("Handshake IO continue\n");
++    vnc_start_vencrypt_handshake(vs);
++}
++
++
++
++#define NEED_X509_AUTH(vs)			      \
++    ((vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509NONE ||   \
++     (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509VNC ||    \
++     (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN)
++
++
++static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len)
++{
++    int auth = read_u32(data, 0);
++
++    if (auth != vs->vd->subauth) {
++	VNC_DEBUG("Rejecting auth %d\n", auth);
++	vnc_write_u8(vs, 0); /* Reject auth */
++	vnc_flush(vs);
++	vnc_client_error(vs);
++    } else {
++	VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth);
++	vnc_write_u8(vs, 1); /* Accept auth */
++	vnc_flush(vs);
++
++	if (vnc_tls_client_setup(vs, NEED_X509_AUTH(vs)) < 0) {
++	    VNC_DEBUG("Failed to setup TLS\n");
++	    return 0;
++	}
++
++	VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
++	if (vnc_start_vencrypt_handshake(vs) < 0) {
++	    VNC_DEBUG("Failed to start TLS handshake\n");
++	    return 0;
++	}
++    }
++    return 0;
++}
++
++static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len)
++{
++    if (data[0] != 0 ||
++	data[1] != 2) {
++	VNC_DEBUG("Unsupported VeNCrypt protocol %d.%d\n", (int)data[0], (int)data[1]);
++	vnc_write_u8(vs, 1); /* Reject version */
++	vnc_flush(vs);
++	vnc_client_error(vs);
++    } else {
++	VNC_DEBUG("Sending allowed auth %d\n", vs->vd->subauth);
++	vnc_write_u8(vs, 0); /* Accept version */
++	vnc_write_u8(vs, 1); /* Number of sub-auths */
++	vnc_write_u32(vs, vs->vd->subauth); /* The supported auth */
++	vnc_flush(vs);
++	vnc_read_when(vs, protocol_client_vencrypt_auth, 4);
++    }
++    return 0;
++}
++
++
++void start_auth_vencrypt(VncState *vs)
++{
++    /* Send VeNCrypt version 0.2 */
++    vnc_write_u8(vs, 0);
++    vnc_write_u8(vs, 2);
++
++    vnc_read_when(vs, protocol_client_vencrypt_init, 2);
++}
++
+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 <anthony@codemonkey.ws>
++ * Copyright (C) 2006 Fabrice Bellard
++ * Copyright (C) 2009 Red Hat, Inc
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++
++#ifndef __QEMU_VNC_AUTH_VENCRYPT_H__
++#define __QEMU_VNC_AUTH_VENCRYPT_H__
++
++void start_auth_vencrypt(VncState *vs);
++
++#endif /* __QEMU_VNC_AUTH_VENCRYPT_H__ */
+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 <anthony@codemonkey.ws>
++ * Copyright (C) 2006 Fabrice Bellard
++ * Copyright (C) 2009 Red Hat, Inc
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++#include "vnc.h"
++#include "qemu_socket.h"
++
++#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
++/* Very verbose, so only enabled for _VNC_DEBUG >= 2 */
++static void vnc_debug_gnutls_log(int level, const char* str) {
++    VNC_DEBUG("%d %s", level, str);
++}
++#endif /* defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 */
++
++
++#define DH_BITS 1024
++static gnutls_dh_params_t dh_params;
++
++static int vnc_tls_initialize(void)
++{
++    static int tlsinitialized = 0;
++
++    if (tlsinitialized)
++	return 1;
++
++    if (gnutls_global_init () < 0)
++	return 0;
++
++    /* XXX ought to re-generate diffie-hellmen params periodically */
++    if (gnutls_dh_params_init (&dh_params) < 0)
++	return 0;
++    if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0)
++	return 0;
++
++#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
++    gnutls_global_set_log_level(10);
++    gnutls_global_set_log_function(vnc_debug_gnutls_log);
++#endif
++
++    tlsinitialized = 1;
++
++    return 1;
++}
++
++static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport,
++			    const void *data,
++			    size_t len) {
++    struct VncState *vs = (struct VncState *)transport;
++    int ret;
++
++ retry:
++    ret = send(vs->csock, data, len, 0);
++    if (ret < 0) {
++	if (errno == EINTR)
++	    goto retry;
++	return -1;
++    }
++    return ret;
++}
++
++
++static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport,
++			    void *data,
++			    size_t len) {
++    struct VncState *vs = (struct VncState *)transport;
++    int ret;
++
++ retry:
++    ret = recv(vs->csock, data, len, 0);
++    if (ret < 0) {
++	if (errno == EINTR)
++	    goto retry;
++	return -1;
++    }
++    return ret;
++}
++
++
++static gnutls_anon_server_credentials vnc_tls_initialize_anon_cred(void)
++{
++    gnutls_anon_server_credentials anon_cred;
++    int ret;
++
++    if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) {
++	VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
++	return NULL;
++    }
++
++    gnutls_anon_set_server_dh_params(anon_cred, dh_params);
++
++    return anon_cred;
++}
++
++
++static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncDisplay *vd)
++{
++    gnutls_certificate_credentials_t x509_cred;
++    int ret;
++
++    if (!vd->tls.x509cacert) {
++	VNC_DEBUG("No CA x509 certificate specified\n");
++	return NULL;
++    }
++    if (!vd->tls.x509cert) {
++	VNC_DEBUG("No server x509 certificate specified\n");
++	return NULL;
++    }
++    if (!vd->tls.x509key) {
++	VNC_DEBUG("No server private key specified\n");
++	return NULL;
++    }
++
++    if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) {
++	VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
++	return NULL;
++    }
++    if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
++						      vd->tls.x509cacert,
++						      GNUTLS_X509_FMT_PEM)) < 0) {
++	VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret));
++	gnutls_certificate_free_credentials(x509_cred);
++	return NULL;
++    }
++
++    if ((ret = gnutls_certificate_set_x509_key_file (x509_cred,
++						     vd->tls.x509cert,
++						     vd->tls.x509key,
++						     GNUTLS_X509_FMT_PEM)) < 0) {
++	VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret));
++	gnutls_certificate_free_credentials(x509_cred);
++	return NULL;
++    }
++
++    if (vd->tls.x509cacrl) {
++	if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
++							vd->tls.x509cacrl,
++							GNUTLS_X509_FMT_PEM)) < 0) {
++	    VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret));
++	    gnutls_certificate_free_credentials(x509_cred);
++	    return NULL;
++	}
++    }
++
++    gnutls_certificate_set_dh_params (x509_cred, dh_params);
++
++    return x509_cred;
++}
++
++
++int vnc_tls_validate_certificate(struct VncState *vs)
++{
++    int ret;
++    unsigned int status;
++    const gnutls_datum_t *certs;
++    unsigned int nCerts, i;
++    time_t now;
++
++    VNC_DEBUG("Validating client certificate\n");
++    if ((ret = gnutls_certificate_verify_peers2 (vs->tls.session, &status)) < 0) {
++	VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret));
++	return -1;
++    }
++
++    if ((now = time(NULL)) == ((time_t)-1)) {
++	return -1;
++    }
++
++    if (status != 0) {
++	if (status & GNUTLS_CERT_INVALID)
++	    VNC_DEBUG("The certificate is not trusted.\n");
++
++	if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
++	    VNC_DEBUG("The certificate hasn't got a known issuer.\n");
++
++	if (status & GNUTLS_CERT_REVOKED)
++	    VNC_DEBUG("The certificate has been revoked.\n");
++
++	if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
++	    VNC_DEBUG("The certificate uses an insecure algorithm\n");
++
++	return -1;
++    } else {
++	VNC_DEBUG("Certificate is valid!\n");
++    }
++
++    /* Only support x509 for now */
++    if (gnutls_certificate_type_get(vs->tls.session) != GNUTLS_CRT_X509)
++	return -1;
++
++    if (!(certs = gnutls_certificate_get_peers(vs->tls.session, &nCerts)))
++	return -1;
++
++    for (i = 0 ; i < nCerts ; i++) {
++	gnutls_x509_crt_t cert;
++	VNC_DEBUG ("Checking certificate chain %d\n", i);
++	if (gnutls_x509_crt_init (&cert) < 0)
++	    return -1;
++
++	if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) {
++	    gnutls_x509_crt_deinit (cert);
++	    return -1;
++	}
++
++	if (gnutls_x509_crt_get_expiration_time (cert) < now) {
++	    VNC_DEBUG("The certificate has expired\n");
++	    gnutls_x509_crt_deinit (cert);
++	    return -1;
++	}
++
++	if (gnutls_x509_crt_get_activation_time (cert) > now) {
++	    VNC_DEBUG("The certificate is not yet activated\n");
++	    gnutls_x509_crt_deinit (cert);
++	    return -1;
++	}
++
++	if (gnutls_x509_crt_get_activation_time (cert) > now) {
++	    VNC_DEBUG("The certificate is not yet activated\n");
++	    gnutls_x509_crt_deinit (cert);
++	    return -1;
++	}
++
++	gnutls_x509_crt_deinit (cert);
++    }
++
++    return 0;
++}
++
++
++int vnc_tls_client_setup(struct VncState *vs,
++			 int needX509Creds) {
++    static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
++    static const int protocol_priority[]= { GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 };
++    static const int kx_anon[] = {GNUTLS_KX_ANON_DH, 0};
++    static const int kx_x509[] = {GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0};
++
++    VNC_DEBUG("Do TLS setup\n");
++    if (vnc_tls_initialize() < 0) {
++	VNC_DEBUG("Failed to init TLS\n");
++	vnc_client_error(vs);
++	return -1;
++    }
++    if (vs->tls.session == NULL) {
++	if (gnutls_init(&vs->tls.session, GNUTLS_SERVER) < 0) {
++	    vnc_client_error(vs);
++	    return -1;
++	}
++
++	if (gnutls_set_default_priority(vs->tls.session) < 0) {
++	    gnutls_deinit(vs->tls.session);
++	    vs->tls.session = NULL;
++	    vnc_client_error(vs);
++	    return -1;
++	}
++
++	if (gnutls_kx_set_priority(vs->tls.session, needX509Creds ? kx_x509 : kx_anon) < 0) {
++	    gnutls_deinit(vs->tls.session);
++	    vs->tls.session = NULL;
++	    vnc_client_error(vs);
++	    return -1;
++	}
++
++	if (gnutls_certificate_type_set_priority(vs->tls.session, cert_type_priority) < 0) {
++	    gnutls_deinit(vs->tls.session);
++	    vs->tls.session = NULL;
++	    vnc_client_error(vs);
++	    return -1;
++	}
++
++	if (gnutls_protocol_set_priority(vs->tls.session, protocol_priority) < 0) {
++	    gnutls_deinit(vs->tls.session);
++	    vs->tls.session = NULL;
++	    vnc_client_error(vs);
++	    return -1;
++	}
++
++	if (needX509Creds) {
++	    gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred(vs->vd);
++	    if (!x509_cred) {
++		gnutls_deinit(vs->tls.session);
++		vs->tls.session = NULL;
++		vnc_client_error(vs);
++		return -1;
++	    }
++	    if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) {
++		gnutls_deinit(vs->tls.session);
++		vs->tls.session = NULL;
++		gnutls_certificate_free_credentials(x509_cred);
++		vnc_client_error(vs);
++		return -1;
++	    }
++	    if (vs->vd->tls.x509verify) {
++		VNC_DEBUG("Requesting a client certificate\n");
++		gnutls_certificate_server_set_request (vs->tls.session, GNUTLS_CERT_REQUEST);
++	    }
++
++	} else {
++	    gnutls_anon_server_credentials anon_cred = vnc_tls_initialize_anon_cred();
++	    if (!anon_cred) {
++		gnutls_deinit(vs->tls.session);
++		vs->tls.session = NULL;
++		vnc_client_error(vs);
++		return -1;
++	    }
++	    if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_ANON, anon_cred) < 0) {
++		gnutls_deinit(vs->tls.session);
++		vs->tls.session = NULL;
++		gnutls_anon_free_server_credentials(anon_cred);
++		vnc_client_error(vs);
++		return -1;
++	    }
++	}
++
++	gnutls_transport_set_ptr(vs->tls.session, (gnutls_transport_ptr_t)vs);
++	gnutls_transport_set_push_function(vs->tls.session, vnc_tls_push);
++	gnutls_transport_set_pull_function(vs->tls.session, vnc_tls_pull);
++    }
++    return 0;
++}
++
++
++void vnc_tls_client_cleanup(struct VncState *vs)
++{
++    if (vs->tls.session) {
++	gnutls_deinit(vs->tls.session);
++	vs->tls.session = NULL;
++    }
++    vs->tls.wiremode = VNC_WIREMODE_CLEAR;
++}
++
++
++
++static int vnc_set_x509_credential(VncDisplay *vd,
++				   const char *certdir,
++				   const char *filename,
++				   char **cred,
++				   int ignoreMissing)
++{
++    struct stat sb;
++
++    if (*cred) {
++	qemu_free(*cred);
++	*cred = NULL;
++    }
++
++    *cred = qemu_malloc(strlen(certdir) + strlen(filename) + 2);
++
++    strcpy(*cred, certdir);
++    strcat(*cred, "/");
++    strcat(*cred, filename);
++
++    VNC_DEBUG("Check %s\n", *cred);
++    if (stat(*cred, &sb) < 0) {
++	qemu_free(*cred);
++	*cred = NULL;
++	if (ignoreMissing && errno == ENOENT)
++	    return 0;
++	return -1;
++    }
++
++    return 0;
++}
++
++
++#define X509_CA_CERT_FILE "ca-cert.pem"
++#define X509_CA_CRL_FILE "ca-crl.pem"
++#define X509_SERVER_KEY_FILE "server-key.pem"
++#define X509_SERVER_CERT_FILE "server-cert.pem"
++
++
++int vnc_tls_set_x509_creds_dir(VncDisplay *vd,
++			       const char *certdir)
++{
++    if (vnc_set_x509_credential(vd, certdir, X509_CA_CERT_FILE, &vd->tls.x509cacert, 0) < 0)
++	goto cleanup;
++    if (vnc_set_x509_credential(vd, certdir, X509_CA_CRL_FILE, &vd->tls.x509cacrl, 1) < 0)
++	goto cleanup;
++    if (vnc_set_x509_credential(vd, certdir, X509_SERVER_CERT_FILE, &vd->tls.x509cert, 0) < 0)
++	goto cleanup;
++    if (vnc_set_x509_credential(vd, certdir, X509_SERVER_KEY_FILE, &vd->tls.x509key, 0) < 0)
++	goto cleanup;
++
++    return 0;
++
++ cleanup:
++    qemu_free(vd->tls.x509cacert);
++    qemu_free(vd->tls.x509cacrl);
++    qemu_free(vd->tls.x509cert);
++    qemu_free(vd->tls.x509key);
++    vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL;
++    return -1;
++}
++
+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 <anthony@codemonkey.ws>
++ * Copyright (C) 2006 Fabrice Bellard
++ * Copyright (C) 2009 Red Hat, Inc
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++
++#ifndef __QEMU_VNC_TLS_H__
++#define __QEMU_VNC_TLS_H__
++
++#include <gnutls/gnutls.h>
++#include <gnutls/x509.h>
++
++enum {
++    VNC_WIREMODE_CLEAR,
++    VNC_WIREMODE_TLS,
++};
++
++typedef struct VncDisplayTLS VncDisplayTLS;
++typedef struct VncStateTLS VncStateTLS;
++
++/* Server state */
++struct VncDisplayTLS {
++    int x509verify; /* Non-zero if server requests & validates client cert */
++
++    /* Paths to x509 certs/keys */
++    char *x509cacert;
++    char *x509cacrl;
++    char *x509cert;
++    char *x509key;
++};
++
++/* Per client state */
++struct VncStateTLS {
++    /* Whether data is being TLS encrypted yet */
++    int wiremode;
++    gnutls_session_t session;
++};
++
++int vnc_tls_client_setup(VncState *vs, int x509Creds);
++void vnc_tls_client_cleanup(VncState *vs);
++
++int vnc_tls_validate_certificate(VncState *vs);
++
++int vnc_tls_set_x509_creds_dir(VncDisplay *vd,
++			       const char *path);
++
++
++#endif /* __QEMU_VNC_TLS_H__ */
++
+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 <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
+@@ -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 */
diff --git a/06-vnc-sasl.patch b/06-vnc-sasl.patch
new file mode 100644
index 0000000..f7eafd0
--- /dev/null
+++ b/06-vnc-sasl.patch
@@ -0,0 +1,1526 @@
+Index: qemu-kvm-0.10/qemu/Makefile
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/Makefile
++++ qemu-kvm-0.10/qemu/Makefile
+@@ -152,6 +152,9 @@ OBJS+=vnc.o d3des.o
+ ifdef CONFIG_VNC_TLS
+ OBJS+=vnc-tls.o vnc-auth-vencrypt.o
+ endif
++ifdef CONFIG_VNC_SASL
++OBJS+=vnc-auth-sasl.o
++endif
+ 
+ ifdef CONFIG_COCOA
+ OBJS+=cocoa.o
+@@ -175,7 +178,7 @@ sdl.o: sdl.c keymaps.h sdl_keysym.h
+ 
+ sdl.o audio/sdlaudio.o: CFLAGS += $(SDL_CFLAGS)
+ 
+-vnc.h: vnc-tls.h vnc-auth-vencrypt.h keymaps.h
++vnc.h: vnc-tls.h vnc-auth-vencrypt.h vnc-auth-sasl.h keymaps.h
+ 
+ vnc.o: vnc.c vnc.h vnc_keysym.h vnchextile.h d3des.c d3des.h
+ 
+@@ -185,6 +188,8 @@ vnc-tls.o: vnc-tls.c vnc.h
+ 
+ vnc-auth-vencrypt.o: vnc-auth-vencrypt.c vnc.h
+ 
++vnc-auth-sasl.o: vnc-auth-sasl.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/Makefile.target
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/Makefile.target
++++ qemu-kvm-0.10/qemu/Makefile.target
+@@ -613,6 +613,11 @@ CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS)
+ LIBS += $(CONFIG_VNC_TLS_LIBS)
+ endif
+ 
++ifdef CONFIG_VNC_SASL
++CPPFLAGS += $(CONFIG_VNC_SASL_CFLAGS)
++LIBS += $(CONFIG_VNC_SASL_LIBS)
++endif
++
+ ifdef CONFIG_BLUEZ
+ LIBS += $(CONFIG_BLUEZ_LIBS)
+ endif
+Index: qemu-kvm-0.10/qemu/configure
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/configure
++++ qemu-kvm-0.10/qemu/configure
+@@ -164,6 +164,7 @@ fmod_lib=""
+ fmod_inc=""
+ oss_lib=""
+ vnc_tls="yes"
++vnc_sasl="yes"
+ bsd="no"
+ linux="no"
+ solaris="no"
+@@ -404,6 +405,8 @@ for opt do
+   ;;
+   --disable-vnc-tls) vnc_tls="no"
+   ;;
++  --disable-vnc-sasl) vnc_sasl="no"
++  ;;
+   --disable-slirp) slirp="no"
+   ;;
+   --disable-vde) vde="no"
+@@ -563,6 +566,7 @@ echo "                           Availab
+ echo "  --enable-mixemu          enable mixer emulation"
+ echo "  --disable-brlapi         disable BrlAPI"
+ echo "  --disable-vnc-tls        disable TLS encryption for VNC server"
++echo "  --disable-vnc-sasl       disable SASL encryption for VNC server"
+ echo "  --disable-curses         disable curses output"
+ echo "  --disable-bluez          disable bluez stack connectivity"
+ echo "  --disable-kvm            disable KVM acceleration support"
+@@ -890,6 +894,25 @@ EOF
+ fi
+ 
+ ##########################################
++# VNC SASL detection
++if test "$vnc_sasl" = "yes" ; then
++cat > $TMPC <<EOF
++#include <sasl/sasl.h>
++#include <stdio.h>
++int main(void) { sasl_server_init(NULL, "qemu"); return 0; }
++EOF
++    # Assuming Cyrus-SASL installed in /usr prefix
++    vnc_sasl_cflags=""
++    vnc_sasl_libs="-lsasl2"
++    if $cc $ARCH_CFLAGS -o $TMPE ${OS_CFLAGS} $vnc_sasl_cflags $TMPC \
++           $vnc_sasl_libs 2> /dev/null ; then
++	:
++    else
++	vnc_sasl="no"
++    fi
++fi
++
++##########################################
+ # vde libraries probe
+ if test "$vde" = "yes" ; then
+   cat > $TMPC << EOF
+@@ -1224,6 +1247,11 @@ if test "$vnc_tls" = "yes" ; then
+     echo "    TLS CFLAGS    $vnc_tls_cflags"
+     echo "    TLS LIBS      $vnc_tls_libs"
+ fi
++echo "VNC SASL support  $vnc_sasl"
++if test "$vnc_sasl" = "yes" ; then
++    echo "    SASL CFLAGS    $vnc_sasl_cflags"
++    echo "    SASL LIBS      $vnc_sasl_libs"
++fi
+ if test -n "$sparc_cpu"; then
+     echo "Target Sparc Arch $sparc_cpu"
+ fi
+@@ -1467,6 +1495,12 @@ if test "$vnc_tls" = "yes" ; then
+   echo "CONFIG_VNC_TLS_LIBS=$vnc_tls_libs" >> $config_mak
+   echo "#define CONFIG_VNC_TLS 1" >> $config_h
+ fi
++if test "$vnc_sasl" = "yes" ; then
++  echo "CONFIG_VNC_SASL=yes" >> $config_mak
++  echo "CONFIG_VNC_SASL_CFLAGS=$vnc_sasl_cflags" >> $config_mak
++  echo "CONFIG_VNC_SASL_LIBS=$vnc_sasl_libs" >> $config_mak
++  echo "#define CONFIG_VNC_SASL 1" >> $config_h
++fi
+ qemu_version=`head $source_path/VERSION`
+ echo "VERSION=$qemu_version" >>$config_mak
+ echo "#define QEMU_VERSION \"$qemu_version\"" >> $config_h
+Index: qemu-kvm-0.10/qemu/qemu-doc.texi
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/qemu-doc.texi
++++ qemu-kvm-0.10/qemu/qemu-doc.texi
+@@ -624,6 +624,21 @@ path following this option specifies whe
+ be loaded from. See the @ref{vnc_security} section for details on generating
+ certificates.
+ 
++@item sasl
++
++Require that the client use SASL to authenticate with the VNC server.
++The exact choice of authentication method used is controlled from the
++system / user's SASL configuration file for the 'qemu' service. This
++is typically found in /etc/sasl2/qemu.conf. If running QEMU as an
++unprivileged user, an environment variable SASL_CONF_PATH can be used
++to make it search alternate locations for the service config.
++While some SASL auth methods can also provide data encryption (eg GSSAPI),
++it is recommended that SASL always be combined with the 'tls' and
++'x509' settings to enable use of SSL and server certificates. This
++ensures a data encryption preventing compromise of authentication
++credentials. See the @ref{vnc_security} section for details on using
++SASL authentication.
++
+ @end table
+ 
+ @end table
+@@ -2069,7 +2084,10 @@ considerations depending on the deployme
+ * vnc_sec_certificate::
+ * vnc_sec_certificate_verify::
+ * vnc_sec_certificate_pw::
++* vnc_sec_sasl::
++* vnc_sec_certificate_sasl::
+ * vnc_generate_cert::
++* vnc_setup_sasl::
+ @end menu
+ @node vnc_sec_none
+ @subsection Without passwords
+@@ -2152,6 +2170,41 @@ Password: ********
+ (qemu)
+ @end example
+ 
++
++@node vnc_sec_sasl
++@subsection With SASL authentication
++
++The SASL authentication method is a VNC extension, that provides an
++easily extendable, pluggable authentication method. This allows for
++integration with a wide range of authentication mechanisms, such as
++PAM, GSSAPI/Kerberos, LDAP, SQL databases, one-time keys and more.
++The strength of the authentication depends on the exact mechanism
++configured. If the chosen mechanism also provides a SSF layer, then
++it will encrypt the datastream as well.
++
++Refer to the later docs on how to choose the exact SASL mechanism
++used for authentication, but assuming use of one supporting SSF,
++then QEMU can be launched with:
++
++@example
++qemu [...OPTIONS...] -vnc :1,sasl -monitor stdio
++@end example
++
++@node vnc_sec_certificate_sasl
++@subsection With x509 certificates and SASL authentication
++
++If the desired SASL authentication mechanism does not supported
++SSF layers, then it is strongly advised to run it in combination
++with TLS and x509 certificates. This provides securely encrypted
++data stream, avoiding risk of compromising of the security
++credentials. This can be enabled, by combining the 'sasl' option
++with the aforementioned TLS + x509 options:
++
++@example
++qemu [...OPTIONS...] -vnc :1,tls,x509,sasl -monitor stdio
++@end example
++
++
+ @node vnc_generate_cert
+ @subsection Generating certificates for VNC
+ 
+@@ -2263,6 +2316,50 @@ EOF
+ The @code{client-key.pem} and @code{client-cert.pem} files should now be securely
+ copied to the client for which they were generated.
+ 
++
++@node vnc_setup_sasl
++
++@subsection Configuring SASL mechanisms
++
++The following documentation assumes use of the Cyrus SASL implementation on a
++Linux host, but the principals should apply to any other SASL impl. When SASL
++is enabled, the mechanism configuration will be loaded from system default
++SASL service config /etc/sasl2/qemu.conf. If running QEMU as an
++unprivileged user, an environment variable SASL_CONF_PATH can be used
++to make it search alternate locations for the service config.
++
++The default configuration might contain
++
++@example
++mech_list: digest-md5
++sasldb_path: /etc/qemu/passwd.db
++@end example
++
++This says to use the 'Digest MD5' mechanism, which is similar to the HTTP
++Digest-MD5 mechanism. The list of valid usernames & passwords is maintained
++in the /etc/qemu/passwd.db file, and can be updated using the saslpasswd2
++command. While this mechanism is easy to configure and use, it is not
++considered secure by modern standards, so only suitable for developers /
++ad-hoc testing.
++
++A more serious deployment might use Kerberos, which is done with the 'gssapi'
++mechanism
++
++@example
++mech_list: gssapi
++keytab: /etc/qemu/krb5.tab
++@end example
++
++For this to work the administrator of your KDC must generate a Kerberos
++principal for the server, with a name of  'qemu/somehost.example.com@@EXAMPLE.COM'
++replacing 'somehost.example.com' with the fully qualified host name of the
++machine running QEMU, and 'EXAMPLE.COM' with the Keberos Realm.
++
++Other configurations will be left as an exercise for the reader. It should
++be noted that only Digest-MD5 and GSSAPI provides a SSF layer for data
++encryption. For all other mechanisms, VNC should always be configured to
++use TLS and x509 certificates to protect security credentials from snooping.
++
+ @node gdb_usage
+ @section GDB usage
+ 
+Index: qemu-kvm-0.10/qemu/qemu.sasl
+===================================================================
+--- /dev/null
++++ qemu-kvm-0.10/qemu/qemu.sasl
+@@ -0,0 +1,34 @@
++# If you want to use the non-TLS socket, then you *must* include
++# the GSSAPI or DIGEST-MD5 mechanisms, because they are the only
++# ones that can offer session encryption as well as authentication.
++#
++# If you're only using TLS, then you can turn on any mechanisms
++# you like for authentication, because TLS provides the encryption
++#
++# Default to a simple username+password mechanism
++# NB digest-md5 is no longer considered secure by current standards
++mech_list: digest-md5
++
++# Before you can use GSSAPI, you need a service principle on the
++# KDC server for libvirt, and that to be exported to the keytab
++# file listed below
++#mech_list: gssapi
++#
++# You can also list many mechanisms at once, then the user can choose
++# by adding  '?auth=sasl.gssapi' to their libvirt URI, eg
++#   qemu+tcp://hostname/system?auth=sasl.gssapi
++#mech_list: digest-md5 gssapi
++
++# Some older builds of MIT kerberos on Linux ignore this option &
++# instead need KRB5_KTNAME env var.
++# For modern Linux, and other OS, this should be sufficient
++keytab: /etc/qemu/krb5.tab
++
++# If using digest-md5 for username/passwds, then this is the file
++# containing the passwds. Use 'saslpasswd2 -a qemu [username]'
++# to add entries, and 'sasldblistusers2 -a qemu' to browse it
++sasldb_path: /etc/qemu/passwd.db
++
++
++auxprop_plugin: sasldb
++
+Index: qemu-kvm-0.10/qemu/vnc-auth-sasl.c
+===================================================================
+--- /dev/null
++++ qemu-kvm-0.10/qemu/vnc-auth-sasl.c
+@@ -0,0 +1,626 @@
++/*
++ * QEMU VNC display driver: SASL auth protocol
++ *
++ * 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"
++
++/* Max amount of data we send/recv for SASL steps to prevent DOS */
++#define SASL_DATA_MAX_LEN (1024 * 1024)
++
++
++void vnc_sasl_client_cleanup(VncState *vs)
++{
++    if (vs->sasl.conn) {
++	vs->sasl.runSSF = vs->sasl.waitWriteSSF = vs->sasl.wantSSF = 0;
++	vs->sasl.encodedLength = vs->sasl.encodedOffset = 0;
++	vs->sasl.encoded = NULL;
++	free(vs->sasl.username);
++	free(vs->sasl.mechlist);
++	vs->sasl.username = vs->sasl.mechlist = NULL;
++	sasl_dispose(&vs->sasl.conn);
++	vs->sasl.conn = NULL;
++    }
++}
++
++
++long vnc_client_write_sasl(VncState *vs)
++{
++    long ret;
++
++    VNC_DEBUG("Write SASL: Pending output %p size %d offset %d Encoded: %p size %d offset %d\n",
++	      vs->output.buffer, vs->output.capacity, vs->output.offset,
++	      vs->sasl.encoded, vs->sasl.encodedLength, vs->sasl.encodedOffset);
++
++    if (!vs->sasl.encoded) {
++	int err;
++	err = sasl_encode(vs->sasl.conn,
++			  (char *)vs->output.buffer,
++			  vs->output.offset,
++			  (const char **)&vs->sasl.encoded,
++			  &vs->sasl.encodedLength);
++	if (err != SASL_OK)
++	    return vnc_client_io_error(vs, -1, EIO);
++
++	vs->sasl.encodedOffset = 0;
++    }
++
++    ret = vnc_client_write_buf(vs,
++			       vs->sasl.encoded + vs->sasl.encodedOffset,
++			       vs->sasl.encodedLength - vs->sasl.encodedOffset);
++    if (!ret)
++	return 0;
++
++    vs->sasl.encodedOffset += ret;
++    if (vs->sasl.encodedOffset == vs->sasl.encodedLength) {
++	vs->output.offset = 0;
++	vs->sasl.encoded = NULL;
++	vs->sasl.encodedOffset = vs->sasl.encodedLength = 0;
++    }
++
++    /* Can't merge this block with one above, because
++     * someone might have written more unencrypted
++     * data in vs->output while we were processing
++     * SASL encoded output
++     */
++    if (vs->output.offset == 0) {
++	qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
++    }
++
++    return ret;
++}
++
++
++long vnc_client_read_sasl(VncState *vs)
++{
++    long ret;
++    uint8_t encoded[4096];
++    const char *decoded;
++    unsigned int decodedLen;
++    int err;
++
++    ret = vnc_client_read_buf(vs, encoded, sizeof(encoded));
++    if (!ret)
++	return 0;
++
++    err = sasl_decode(vs->sasl.conn,
++		      (char *)encoded, ret,
++		      &decoded, &decodedLen);
++
++    if (err != SASL_OK)
++	return vnc_client_io_error(vs, -1, -EIO);
++    VNC_DEBUG("Read SASL Encoded %p size %ld Decoded %p size %d\n",
++	      encoded, ret, decoded, decodedLen);
++    buffer_reserve(&vs->input, decodedLen);
++    buffer_append(&vs->input, decoded, decodedLen);
++    return decodedLen;
++}
++
++
++static int vnc_auth_sasl_check_access(VncState *vs)
++{
++    const void *val;
++    int err;
++
++    err = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val);
++    if (err != SASL_OK) {
++	VNC_DEBUG("cannot query SASL username on connection %d (%s)\n",
++		  err, sasl_errstring(err, NULL, NULL));
++	return -1;
++    }
++    if (val == NULL) {
++	VNC_DEBUG("no client username was found\n");
++	return -1;
++    }
++    VNC_DEBUG("SASL client username %s\n", (const char *)val);
++
++    vs->sasl.username = qemu_strdup((const char*)val);
++
++    return 0;
++}
++
++static int vnc_auth_sasl_check_ssf(VncState *vs)
++{
++    const void *val;
++    int err, ssf;
++
++    if (!vs->sasl.wantSSF)
++	return 1;
++
++    err = sasl_getprop(vs->sasl.conn, SASL_SSF, &val);
++    if (err != SASL_OK)
++	return 0;
++
++    ssf = *(const int *)val;
++    VNC_DEBUG("negotiated an SSF of %d\n", ssf);
++    if (ssf < 56)
++	return 0; /* 56 is good for Kerberos */
++
++    /* Only setup for read initially, because we're about to send an RPC
++     * reply which must be in plain text. When the next incoming RPC
++     * arrives, we'll switch on writes too
++     *
++     * cf qemudClientReadSASL  in qemud.c
++     */
++    vs->sasl.runSSF = 1;
++
++    /* We have a SSF that's good enough */
++    return 1;
++}
++
++/*
++ * Step Msg
++ *
++ * Input from client:
++ *
++ * u32 clientin-length
++ * u8-array clientin-string
++ *
++ * Output to client:
++ *
++ * u32 serverout-length
++ * u8-array serverout-strin
++ * u8 continue
++ */
++
++static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len);
++
++static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t len)
++{
++    uint32_t datalen = len;
++    const char *serverout;
++    unsigned int serveroutlen;
++    int err;
++    char *clientdata = NULL;
++
++    /* NB, distinction of NULL vs "" is *critical* in SASL */
++    if (datalen) {
++	clientdata = (char*)data;
++	clientdata[datalen-1] = '\0'; /* Wire includes '\0', but make sure */
++	datalen--; /* Don't count NULL byte when passing to _start() */
++    }
++
++    VNC_DEBUG("Step using SASL Data %p (%d bytes)\n",
++	      clientdata, datalen);
++    err = sasl_server_step(vs->sasl.conn,
++			   clientdata,
++			   datalen,
++			   &serverout,
++			   &serveroutlen);
++    if (err != SASL_OK &&
++	err != SASL_CONTINUE) {
++	VNC_DEBUG("sasl step failed %d (%s)\n",
++		  err, sasl_errdetail(vs->sasl.conn));
++	sasl_dispose(&vs->sasl.conn);
++	vs->sasl.conn = NULL;
++	goto authabort;
++    }
++
++    if (serveroutlen > SASL_DATA_MAX_LEN) {
++	VNC_DEBUG("sasl step reply data too long %d\n",
++		  serveroutlen);
++	sasl_dispose(&vs->sasl.conn);
++	vs->sasl.conn = NULL;
++	goto authabort;
++    }
++
++    VNC_DEBUG("SASL return data %d bytes, nil; %d\n",
++	      serveroutlen, serverout ? 0 : 1);
++
++    if (serveroutlen) {
++	vnc_write_u32(vs, serveroutlen + 1);
++	vnc_write(vs, serverout, serveroutlen + 1);
++    } else {
++	vnc_write_u32(vs, 0);
++    }
++
++    /* Whether auth is complete */
++    vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1);
++
++    if (err == SASL_CONTINUE) {
++	VNC_DEBUG("%s", "Authentication must continue\n");
++	/* Wait for step length */
++	vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
++    } else {
++	if (!vnc_auth_sasl_check_ssf(vs)) {
++	    VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock);
++	    goto authreject;
++	}
++
++	/* Check username whitelist ACL */
++	if (vnc_auth_sasl_check_access(vs) < 0) {
++	    VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
++	    goto authreject;
++	}
++
++	VNC_DEBUG("Authentication successful %d\n", vs->csock);
++	vnc_write_u32(vs, 0); /* Accept auth */
++	/*
++	 * Delay writing in SSF encoded mode until pending output
++	 * buffer is written
++	 */
++	if (vs->sasl.runSSF)
++	    vs->sasl.waitWriteSSF = vs->output.offset;
++	start_client_init(vs);
++    }
++
++    return 0;
++
++ authreject:
++    vnc_write_u32(vs, 1); /* Reject auth */
++    vnc_write_u32(vs, sizeof("Authentication failed"));
++    vnc_write(vs, "Authentication failed", sizeof("Authentication failed"));
++    vnc_flush(vs);
++    vnc_client_error(vs);
++    return -1;
++
++ authabort:
++    vnc_client_error(vs);
++    return -1;
++}
++
++static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len)
++{
++    uint32_t steplen = read_u32(data, 0);
++    VNC_DEBUG("Got client step len %d\n", steplen);
++    if (steplen > SASL_DATA_MAX_LEN) {
++	VNC_DEBUG("Too much SASL data %d\n", steplen);
++	vnc_client_error(vs);
++	return -1;
++    }
++
++    if (steplen == 0)
++	return protocol_client_auth_sasl_step(vs, NULL, 0);
++    else
++	vnc_read_when(vs, protocol_client_auth_sasl_step, steplen);
++    return 0;
++}
++
++/*
++ * Start Msg
++ *
++ * Input from client:
++ *
++ * u32 clientin-length
++ * u8-array clientin-string
++ *
++ * Output to client:
++ *
++ * u32 serverout-length
++ * u8-array serverout-strin
++ * u8 continue
++ */
++
++#define SASL_DATA_MAX_LEN (1024 * 1024)
++
++static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t len)
++{
++    uint32_t datalen = len;
++    const char *serverout;
++    unsigned int serveroutlen;
++    int err;
++    char *clientdata = NULL;
++
++    /* NB, distinction of NULL vs "" is *critical* in SASL */
++    if (datalen) {
++	clientdata = (char*)data;
++	clientdata[datalen-1] = '\0'; /* Should be on wire, but make sure */
++	datalen--; /* Don't count NULL byte when passing to _start() */
++    }
++
++    VNC_DEBUG("Start SASL auth with mechanism %s. Data %p (%d bytes)\n",
++	      vs->sasl.mechlist, clientdata, datalen);
++    err = sasl_server_start(vs->sasl.conn,
++			    vs->sasl.mechlist,
++			    clientdata,
++			    datalen,
++			    &serverout,
++			    &serveroutlen);
++    if (err != SASL_OK &&
++	err != SASL_CONTINUE) {
++	VNC_DEBUG("sasl start failed %d (%s)\n",
++		  err, sasl_errdetail(vs->sasl.conn));
++	sasl_dispose(&vs->sasl.conn);
++	vs->sasl.conn = NULL;
++	goto authabort;
++    }
++    if (serveroutlen > SASL_DATA_MAX_LEN) {
++	VNC_DEBUG("sasl start reply data too long %d\n",
++		  serveroutlen);
++	sasl_dispose(&vs->sasl.conn);
++	vs->sasl.conn = NULL;
++	goto authabort;
++    }
++
++    VNC_DEBUG("SASL return data %d bytes, nil; %d\n",
++	      serveroutlen, serverout ? 0 : 1);
++
++    if (serveroutlen) {
++	vnc_write_u32(vs, serveroutlen + 1);
++	vnc_write(vs, serverout, serveroutlen + 1);
++    } else {
++	vnc_write_u32(vs, 0);
++    }
++
++    /* Whether auth is complete */
++    vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1);
++
++    if (err == SASL_CONTINUE) {
++	VNC_DEBUG("%s", "Authentication must continue\n");
++	/* Wait for step length */
++	vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
++    } else {
++	if (!vnc_auth_sasl_check_ssf(vs)) {
++	    VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock);
++	    goto authreject;
++	}
++
++	/* Check username whitelist ACL */
++	if (vnc_auth_sasl_check_access(vs) < 0) {
++	    VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
++	    goto authreject;
++	}
++
++	VNC_DEBUG("Authentication successful %d\n", vs->csock);
++	vnc_write_u32(vs, 0); /* Accept auth */
++	start_client_init(vs);
++    }
++
++    return 0;
++
++ authreject:
++    vnc_write_u32(vs, 1); /* Reject auth */
++    vnc_write_u32(vs, sizeof("Authentication failed"));
++    vnc_write(vs, "Authentication failed", sizeof("Authentication failed"));
++    vnc_flush(vs);
++    vnc_client_error(vs);
++    return -1;
++
++ authabort:
++    vnc_client_error(vs);
++    return -1;
++}
++
++static int protocol_client_auth_sasl_start_len(VncState *vs, uint8_t *data, size_t len)
++{
++    uint32_t startlen = read_u32(data, 0);
++    VNC_DEBUG("Got client start len %d\n", startlen);
++    if (startlen > SASL_DATA_MAX_LEN) {
++	VNC_DEBUG("Too much SASL data %d\n", startlen);
++	vnc_client_error(vs);
++	return -1;
++    }
++
++    if (startlen == 0)
++	return protocol_client_auth_sasl_start(vs, NULL, 0);
++
++    vnc_read_when(vs, protocol_client_auth_sasl_start, startlen);
++    return 0;
++}
++
++static int protocol_client_auth_sasl_mechname(VncState *vs, uint8_t *data, size_t len)
++{
++    char *mechname = malloc(len + 1);
++    if (!mechname) {
++	VNC_DEBUG("Out of memory reading mechname\n");
++	vnc_client_error(vs);
++    }
++    strncpy(mechname, (char*)data, len);
++    mechname[len] = '\0';
++    VNC_DEBUG("Got client mechname '%s' check against '%s'\n",
++	      mechname, vs->sasl.mechlist);
++
++    if (strncmp(vs->sasl.mechlist, mechname, len) == 0) {
++	if (vs->sasl.mechlist[len] != '\0' &&
++	    vs->sasl.mechlist[len] != ',') {
++	    VNC_DEBUG("One %d", vs->sasl.mechlist[len]);
++	    vnc_client_error(vs);
++	    return -1;
++	}
++    } else {
++	char *offset = strstr(vs->sasl.mechlist, mechname);
++	VNC_DEBUG("Two %p\n", offset);
++	if (!offset) {
++	    vnc_client_error(vs);
++	    return -1;
++	}
++	VNC_DEBUG("Two '%s'\n", offset);
++	if (offset[-1] != ',' ||
++	    (offset[len] != '\0'&&
++	     offset[len] != ',')) {
++	    vnc_client_error(vs);
++	    return -1;
++	}
++    }
++
++    free(vs->sasl.mechlist);
++    vs->sasl.mechlist = mechname;
++
++    VNC_DEBUG("Validated mechname '%s'\n", mechname);
++    vnc_read_when(vs, protocol_client_auth_sasl_start_len, 4);
++    return 0;
++}
++
++static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, size_t len)
++{
++    uint32_t mechlen = read_u32(data, 0);
++    VNC_DEBUG("Got client mechname len %d\n", mechlen);
++    if (mechlen > 100) {
++	VNC_DEBUG("Too long SASL mechname data %d\n", mechlen);
++	vnc_client_error(vs);
++	return -1;
++    }
++    if (mechlen < 1) {
++	VNC_DEBUG("Too short SASL mechname %d\n", mechlen);
++	vnc_client_error(vs);
++	return -1;
++    }
++    vnc_read_when(vs, protocol_client_auth_sasl_mechname,mechlen);
++    return 0;
++}
++
++#define USES_X509_AUTH(vs)			      \
++    ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE ||   \
++     (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC ||    \
++     (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN ||  \
++     (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL)
++
++
++void start_auth_sasl(VncState *vs)
++{
++    const char *mechlist = NULL;
++    sasl_security_properties_t secprops;
++    int err;
++    char *localAddr, *remoteAddr;
++    int mechlistlen;
++
++    VNC_DEBUG("Initialize SASL auth %d\n", vs->csock);
++
++    /* Get local & remote client addresses in form  IPADDR;PORT */
++    if (!(localAddr = vnc_socket_local_addr("%s;%s", vs->csock)))
++	goto authabort;
++
++    if (!(remoteAddr = vnc_socket_remote_addr("%s;%s", vs->csock))) {
++	free(localAddr);
++	goto authabort;
++    }
++
++    err = sasl_server_new("vnc",
++			  NULL, /* FQDN - just delegates to gethostname */
++			  NULL, /* User realm */
++			  localAddr,
++			  remoteAddr,
++			  NULL, /* Callbacks, not needed */
++			  SASL_SUCCESS_DATA,
++			  &vs->sasl.conn);
++    free(localAddr);
++    free(remoteAddr);
++    localAddr = remoteAddr = NULL;
++
++    if (err != SASL_OK) {
++	VNC_DEBUG("sasl context setup failed %d (%s)",
++		  err, sasl_errstring(err, NULL, NULL));
++	vs->sasl.conn = NULL;
++	goto authabort;
++    }
++
++#ifdef CONFIG_VNC_TLS
++    /* Inform SASL that we've got an external SSF layer from TLS/x509 */
++    if (vs->vd->auth == VNC_AUTH_VENCRYPT &&
++	vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) {
++	gnutls_cipher_algorithm_t cipher;
++	sasl_ssf_t ssf;
++
++	cipher = gnutls_cipher_get(vs->tls.session);
++	if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) {
++	    VNC_DEBUG("%s", "cannot TLS get cipher size\n");
++	    sasl_dispose(&vs->sasl.conn);
++	    vs->sasl.conn = NULL;
++	    goto authabort;
++	}
++	ssf *= 8; /* tls key size is bytes, sasl wants bits */
++
++	err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf);
++	if (err != SASL_OK) {
++	    VNC_DEBUG("cannot set SASL external SSF %d (%s)\n",
++		      err, sasl_errstring(err, NULL, NULL));
++	    sasl_dispose(&vs->sasl.conn);
++	    vs->sasl.conn = NULL;
++	    goto authabort;
++	}
++    } else
++#endif /* CONFIG_VNC_TLS */
++	vs->sasl.wantSSF = 1;
++
++    memset (&secprops, 0, sizeof secprops);
++    /* Inform SASL that we've got an external SSF layer from TLS */
++    if (strncmp(vs->vd->display, "unix:", 5) == 0
++#ifdef CONFIG_VNC_TLS
++	/* Disable SSF, if using TLS+x509+SASL only. TLS without x509
++	   is not sufficiently strong */
++	|| (vs->vd->auth == VNC_AUTH_VENCRYPT &&
++	    vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL)
++#endif /* CONFIG_VNC_TLS */
++	) {
++	/* If we've got TLS or UNIX domain sock, we don't care about SSF */
++	secprops.min_ssf = 0;
++	secprops.max_ssf = 0;
++	secprops.maxbufsize = 8192;
++	secprops.security_flags = 0;
++    } else {
++	/* Plain TCP, better get an SSF layer */
++	secprops.min_ssf = 56; /* Good enough to require kerberos */
++	secprops.max_ssf = 100000; /* Arbitrary big number */
++	secprops.maxbufsize = 8192;
++	/* Forbid any anonymous or trivially crackable auth */
++	secprops.security_flags =
++	    SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
++    }
++
++    err = sasl_setprop(vs->sasl.conn, SASL_SEC_PROPS, &secprops);
++    if (err != SASL_OK) {
++	VNC_DEBUG("cannot set SASL security props %d (%s)\n",
++		  err, sasl_errstring(err, NULL, NULL));
++	sasl_dispose(&vs->sasl.conn);
++	vs->sasl.conn = NULL;
++	goto authabort;
++    }
++
++    err = sasl_listmech(vs->sasl.conn,
++			NULL, /* Don't need to set user */
++			"", /* Prefix */
++			",", /* Separator */
++			"", /* Suffix */
++			&mechlist,
++			NULL,
++			NULL);
++    if (err != SASL_OK) {
++	VNC_DEBUG("cannot list SASL mechanisms %d (%s)\n",
++		  err, sasl_errdetail(vs->sasl.conn));
++	sasl_dispose(&vs->sasl.conn);
++	vs->sasl.conn = NULL;
++	goto authabort;
++    }
++    VNC_DEBUG("Available mechanisms for client: '%s'\n", mechlist);
++
++    if (!(vs->sasl.mechlist = strdup(mechlist))) {
++	VNC_DEBUG("Out of memory");
++	sasl_dispose(&vs->sasl.conn);
++	vs->sasl.conn = NULL;
++	goto authabort;
++    }
++    mechlistlen = strlen(mechlist);
++    vnc_write_u32(vs, mechlistlen);
++    vnc_write(vs, mechlist, mechlistlen);
++    vnc_flush(vs);
++
++    VNC_DEBUG("Wait for client mechname length\n");
++    vnc_read_when(vs, protocol_client_auth_sasl_mechname_len, 4);
++
++    return;
++
++ authabort:
++    vnc_client_error(vs);
++    return;
++}
++
++
+Index: qemu-kvm-0.10/qemu/vnc-auth-sasl.h
+===================================================================
+--- /dev/null
++++ qemu-kvm-0.10/qemu/vnc-auth-sasl.h
+@@ -0,0 +1,67 @@
++/*
++ * QEMU VNC display driver: SASL auth protocol
++ *
++ * 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_SASL_H__
++#define __QEMU_VNC_AUTH_SASL_H__
++
++
++#include <sasl/sasl.h>
++
++typedef struct VncStateSASL VncStateSASL;
++
++struct VncStateSASL {
++    sasl_conn_t *conn;
++    /* If we want to negotiate an SSF layer with client */
++    int wantSSF :1;
++    /* If we are now running the SSF layer */
++    int runSSF :1;
++    /*
++     * If this is non-zero, then wait for that many bytes
++     * to be written plain, before switching to SSF encoding
++     * This allows the VNC auth result to finish being
++     * written in plain.
++     */
++    unsigned int waitWriteSSF;
++
++    /*
++     * Buffering encoded data to allow more clear data
++     * to be stuffed onto the output buffer
++     */
++    const uint8_t *encoded;
++    unsigned int encodedLength;
++    unsigned int encodedOffset;
++    char *username;
++    char *mechlist;
++};
++
++void vnc_sasl_client_cleanup(VncState *vs);
++
++long vnc_client_read_sasl(VncState *vs);
++long vnc_client_write_sasl(VncState *vs);
++
++void start_auth_sasl(VncState *vs);
++
++#endif /* __QEMU_VNC_AUTH_SASL_H__ */
++
+Index: qemu-kvm-0.10/qemu/vnc-auth-vencrypt.c
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/vnc-auth-vencrypt.c
++++ qemu-kvm-0.10/qemu/vnc-auth-vencrypt.c
+@@ -43,8 +43,15 @@ static void start_auth_vencrypt_subauth(
+        start_auth_vnc(vs);
+        break;
+ 
++#ifdef CONFIG_VNC_SASL
++    case VNC_AUTH_VENCRYPT_TLSSASL:
++    case VNC_AUTH_VENCRYPT_X509SASL:
++      VNC_DEBUG("Start TLS auth SASL\n");
++      return start_auth_sasl(vs);
++#endif /* CONFIG_VNC_SASL */
++
+     default: /* Should not be possible, but just in case */
+-       VNC_DEBUG("Reject auth %d\n", vs->vd->auth);
++       VNC_DEBUG("Reject subauth %d server bug\n", vs->vd->auth);
+        vnc_write_u8(vs, 1);
+        if (vs->minor >= 8) {
+            static const char err[] = "Unsupported authentication type";
+@@ -105,7 +112,8 @@ static void vnc_tls_handshake_io(void *o
+ #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)
++     (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN ||  \
++     (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL)
+ 
+ 
+ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len)
+Index: qemu-kvm-0.10/qemu/vnc.c
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/vnc.c
++++ qemu-kvm-0.10/qemu/vnc.c
+@@ -68,7 +68,8 @@ static char *addr_to_string(const char *
+     return addr;
+ }
+ 
+-static char *vnc_socket_local_addr(const char *format, int fd) {
++
++char *vnc_socket_local_addr(const char *format, int fd) {
+     struct sockaddr_storage sa;
+     socklen_t salen;
+ 
+@@ -79,7 +80,8 @@ static char *vnc_socket_local_addr(const
+     return addr_to_string(format, &sa, salen);
+ }
+ 
+-static char *vnc_socket_remote_addr(const char *format, int fd) {
++
++char *vnc_socket_remote_addr(const char *format, int fd) {
+     struct sockaddr_storage sa;
+     socklen_t salen;
+ 
+@@ -125,12 +127,18 @@ static const char *vnc_auth_name(VncDisp
+ 	    return "vencrypt+x509+vnc";
+ 	case VNC_AUTH_VENCRYPT_X509PLAIN:
+ 	    return "vencrypt+x509+plain";
++	case VNC_AUTH_VENCRYPT_TLSSASL:
++	    return "vencrypt+tls+sasl";
++	case VNC_AUTH_VENCRYPT_X509SASL:
++	    return "vencrypt+x509+sasl";
+ 	default:
+ 	    return "vencrypt";
+ 	}
+ #else
+ 	return "vencrypt";
+ #endif
++    case VNC_AUTH_SASL:
++	return "sasl";
+     }
+     return "unknown";
+ }
+@@ -278,7 +286,7 @@ static void vnc_framebuffer_update(VncSt
+     vnc_write_s32(vs, encoding);
+ }
+ 
+-static void buffer_reserve(Buffer *buffer, size_t len)
++void buffer_reserve(Buffer *buffer, size_t len)
+ {
+     if ((buffer->capacity - buffer->offset) < len) {
+ 	buffer->capacity += (len + 1024);
+@@ -290,22 +298,22 @@ static void buffer_reserve(Buffer *buffe
+     }
+ }
+ 
+-static int buffer_empty(Buffer *buffer)
++int buffer_empty(Buffer *buffer)
+ {
+     return buffer->offset == 0;
+ }
+ 
+-static uint8_t *buffer_end(Buffer *buffer)
++uint8_t *buffer_end(Buffer *buffer)
+ {
+     return buffer->buffer + buffer->offset;
+ }
+ 
+-static void buffer_reset(Buffer *buffer)
++void buffer_reset(Buffer *buffer)
+ {
+ 	buffer->offset = 0;
+ }
+ 
+-static void buffer_append(Buffer *buffer, const void *data, size_t len)
++void buffer_append(Buffer *buffer, const void *data, size_t len)
+ {
+     memcpy(buffer->buffer + buffer->offset, data, len);
+     buffer->offset += len;
+@@ -821,7 +829,8 @@ static void audio_del(VncState *vs)
+     }
+ }
+ 
+-static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
++
++int vnc_client_io_error(VncState *vs, int ret, int last_errno)
+ {
+     if (ret == 0 || ret == -1) {
+         if (ret == -1) {
+@@ -847,6 +856,9 @@ static int vnc_client_io_error(VncState 
+ #ifdef CONFIG_VNC_TLS
+ 	vnc_tls_client_cleanup(vs);
+ #endif /* CONFIG_VNC_TLS */
++#ifdef CONFIG_VNC_SASL
++        vnc_sasl_client_cleanup(vs);
++#endif /* CONFIG_VNC_SASL */
+         audio_del(vs);
+ 
+         VncState *p, *parent = NULL;
+@@ -877,14 +889,28 @@ void vnc_client_error(VncState *vs)
+     vnc_client_io_error(vs, -1, EINVAL);
+ }
+ 
+-void vnc_client_write(void *opaque)
++
++/*
++ * Called to write a chunk of data to the client socket. The data may
++ * be the raw data, or may have already been encoded by SASL.
++ * The data will be written either straight onto the socket, or
++ * written via the GNUTLS wrappers, if TLS/SSL encryption is enabled
++ *
++ * NB, it is theoretically possible to have 2 layers of encryption,
++ * both SASL, and this TLS layer. It is highly unlikely in practice
++ * though, since SASL encryption will typically be a no-op if TLS
++ * is active
++ *
++ * Returns the number of bytes written, which may be less than
++ * the requested 'datalen' if the socket would block. Returns
++ * -1 on error, and disconnects the client socket.
++ */
++long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
+ {
+     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);
++	ret = gnutls_write(vs->tls.session, data, datalen);
+ 	if (ret < 0) {
+ 	    if (ret == GNUTLS_E_AGAIN)
+ 		errno = EAGAIN;
+@@ -894,10 +920,42 @@ void vnc_client_write(void *opaque)
+ 	}
+     } else
+ #endif /* CONFIG_VNC_TLS */
+-	ret = send(vs->csock, vs->output.buffer, vs->output.offset, 0);
+-    ret = vnc_client_io_error(vs, ret, socket_error());
++	ret = send(vs->csock, data, datalen, 0);
++    VNC_DEBUG("Wrote wire %p %d -> %ld\n", data, datalen, ret);
++    return vnc_client_io_error(vs, ret, socket_error());
++}
++
++
++/*
++ * Called to write buffered data to the client socket, when not
++ * using any SASL SSF encryption layers. Will write as much data
++ * as possible without blocking. If all buffered data is written,
++ * will switch the FD poll() handler back to read monitoring.
++ *
++ * Returns the number of bytes written, which may be less than
++ * the buffered output data if the socket would block. Returns
++ * -1 on error, and disconnects the client socket.
++ */
++static long vnc_client_write_plain(VncState *vs)
++{
++    long ret;
++
++#ifdef CONFIG_VNC_SASL
++    VNC_DEBUG("Write Plain: Pending output %p size %d offset %d. Wait SSF %d\n",
++              vs->output.buffer, vs->output.capacity, vs->output.offset,
++              vs->sasl.waitWriteSSF);
++
++    if (vs->sasl.conn &&
++        vs->sasl.runSSF &&
++        vs->sasl.waitWriteSSF) {
++        ret = vnc_client_write_buf(vs, vs->output.buffer, vs->sasl.waitWriteSSF);
++        if (ret)
++            vs->sasl.waitWriteSSF -= ret;
++    } else
++#endif /* CONFIG_VNC_SASL */
++        ret = vnc_client_write_buf(vs, vs->output.buffer, vs->output.offset);
+     if (!ret)
+-	return;
++        return 0;
+ 
+     memmove(vs->output.buffer, vs->output.buffer + ret, (vs->output.offset - ret));
+     vs->output.offset -= ret;
+@@ -905,6 +963,29 @@ void vnc_client_write(void *opaque)
+     if (vs->output.offset == 0) {
+ 	qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+     }
++
++    return ret;
++}
++
++
++/*
++ * First function called whenever there is data to be written to
++ * the client socket. Will delegate actual work according to whether
++ * SASL SSF layers are enabled (thus requiring encryption calls)
++ */
++void vnc_client_write(void *opaque)
++{
++    long ret;
++    VncState *vs = opaque;
++
++#ifdef CONFIG_VNC_SASL
++    if (vs->sasl.conn &&
++        vs->sasl.runSSF &&
++        !vs->sasl.waitWriteSSF)
++        ret = vnc_client_write_sasl(vs);
++    else
++#endif /* CONFIG_VNC_SASL */
++        ret = vnc_client_write_plain(vs);
+ }
+ 
+ void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
+@@ -913,16 +994,28 @@ void vnc_read_when(VncState *vs, VncRead
+     vs->read_handler_expect = expecting;
+ }
+ 
+-void vnc_client_read(void *opaque)
++
++/*
++ * Called to read a chunk of data from the client socket. The data may
++ * be the raw data, or may need to be further decoded by SASL.
++ * The data will be read either straight from to the socket, or
++ * read via the GNUTLS wrappers, if TLS/SSL encryption is enabled
++ *
++ * NB, it is theoretically possible to have 2 layers of encryption,
++ * both SASL, and this TLS layer. It is highly unlikely in practice
++ * though, since SASL encryption will typically be a no-op if TLS
++ * is active
++ *
++ * Returns the number of bytes read, which may be less than
++ * the requested 'datalen' if the socket would block. Returns
++ * -1 on error, and disconnects the client socket.
++ */
++long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
+ {
+-    VncState *vs = opaque;
+     long ret;
+-
+-    buffer_reserve(&vs->input, 4096);
+-
+ #ifdef CONFIG_VNC_TLS
+     if (vs->tls.session) {
+-	ret = gnutls_read(vs->tls.session, buffer_end(&vs->input), 4096);
++	ret = gnutls_read(vs->tls.session, data, datalen);
+ 	if (ret < 0) {
+ 	    if (ret == GNUTLS_E_AGAIN)
+ 		errno = EAGAIN;
+@@ -932,12 +1025,52 @@ void vnc_client_read(void *opaque)
+ 	}
+     } else
+ #endif /* CONFIG_VNC_TLS */
+-	ret = recv(vs->csock, buffer_end(&vs->input), 4096, 0);
+-    ret = vnc_client_io_error(vs, ret, socket_error());
+-    if (!ret)
+-	return;
++	ret = recv(vs->csock, data, datalen, 0);
++    VNC_DEBUG("Read wire %p %d -> %ld\n", data, datalen, ret);
++    return vnc_client_io_error(vs, ret, socket_error());
++}
+ 
++
++/*
++ * Called to read data from the client socket to the input buffer,
++ * when not using any SASL SSF encryption layers. Will read as much
++ * data as possible without blocking.
++ *
++ * Returns the number of bytes read. Returns -1 on error, and
++ * disconnects the client socket.
++ */
++static long vnc_client_read_plain(VncState *vs)
++{
++    int ret;
++    VNC_DEBUG("Read plain %p size %d offset %d\n",
++              vs->input.buffer, vs->input.capacity, vs->input.offset);
++    buffer_reserve(&vs->input, 4096);
++    ret = vnc_client_read_buf(vs, buffer_end(&vs->input), 4096);
++    if (!ret)
++        return 0;
+     vs->input.offset += ret;
++    return ret;
++}
++
++
++/*
++ * First function called whenever there is more data to be read from
++ * the client socket. Will delegate actual work according to whether
++ * SASL SSF layers are enabled (thus requiring decryption calls)
++ */
++void vnc_client_read(void *opaque)
++{
++    VncState *vs = opaque;
++    long ret;
++
++#ifdef CONFIG_VNC_SASL
++    if (vs->sasl.conn && vs->sasl.runSSF)
++        ret = vnc_client_read_sasl(vs);
++    else
++#endif /* CONFIG_VNC_SASL */
++        ret = vnc_client_read_plain(vs);
++    if (!ret)
++	return;
+ 
+     while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) {
+ 	size_t len = vs->read_handler_expect;
+@@ -1722,6 +1855,13 @@ static int protocol_client_auth(VncState
+            break;
+ #endif /* CONFIG_VNC_TLS */
+ 
++#ifdef CONFIG_VNC_SASL
++       case VNC_AUTH_SASL:
++           VNC_DEBUG("Accept SASL auth\n");
++           start_auth_sasl(vs);
++           break;
++#endif /* CONFIG_VNC_SASL */
++
+        default: /* Should not be possible, but just in case */
+            VNC_DEBUG("Reject auth %d\n", vs->vd->auth);
+            vnc_write_u8(vs, 1);
+@@ -1923,6 +2063,10 @@ int vnc_display_open(DisplayState *ds, c
+ #ifdef CONFIG_VNC_TLS
+     int tls = 0, x509 = 0;
+ #endif
++#ifdef CONFIG_VNC_SASL
++    int sasl = 0;
++    int saslErr;
++#endif
+ 
+     if (!vnc_display)
+         return -1;
+@@ -1942,6 +2086,10 @@ int vnc_display_open(DisplayState *ds, c
+ 	    reverse = 1;
+ 	} else if (strncmp(options, "to=", 3) == 0) {
+             to_port = atoi(options+3) + 5900;
++#ifdef CONFIG_VNC_SASL
++	} else if (strncmp(options, "sasl", 4) == 0) {
++	    sasl = 1; /* Require SASL auth */
++#endif
+ #ifdef CONFIG_VNC_TLS
+ 	} else if (strncmp(options, "tls", 3) == 0) {
+ 	    tls = 1; /* Require TLS */
+@@ -1978,6 +2126,22 @@ int vnc_display_open(DisplayState *ds, c
+ 	}
+     }
+ 
++    /*
++     * Combinations we support here:
++     *
++     *  - no-auth                (clear text, no auth)
++     *  - password               (clear text, weak auth)
++     *  - sasl                   (encrypt, good auth *IF* using Kerberos via GSSAPI)
++     *  - tls                    (encrypt, weak anonymous creds, no auth)
++     *  - tls + password         (encrypt, weak anonymous creds, weak auth)
++     *  - tls + sasl             (encrypt, weak anonymous creds, good auth)
++     *  - tls + x509             (encrypt, good x509 creds, no auth)
++     *  - tls + x509 + password  (encrypt, good x509 creds, weak auth)
++     *  - tls + x509 + sasl      (encrypt, good x509 creds, good auth)
++     *
++     * NB1. TLS is a stackable auth scheme.
++     * NB2. the x509 schemes have option to validate a client cert dname
++     */
+     if (password) {
+ #ifdef CONFIG_VNC_TLS
+ 	if (tls) {
+@@ -1990,13 +2154,34 @@ int vnc_display_open(DisplayState *ds, c
+ 		vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
+ 	    }
+ 	} else {
+-#endif
++#endif /* CONFIG_VNC_TLS */
+ 	    VNC_DEBUG("Initializing VNC server with password auth\n");
+ 	    vs->auth = VNC_AUTH_VNC;
+ #ifdef CONFIG_VNC_TLS
+ 	    vs->subauth = VNC_AUTH_INVALID;
+ 	}
+-#endif
++#endif /* CONFIG_VNC_TLS */
++#ifdef CONFIG_VNC_SASL
++    } else if (sasl) {
++#ifdef CONFIG_VNC_TLS
++        if (tls) {
++            vs->auth = VNC_AUTH_VENCRYPT;
++            if (x509) {
++		VNC_DEBUG("Initializing VNC server with x509 SASL auth\n");
++                vs->subauth = VNC_AUTH_VENCRYPT_X509SASL;
++            } else {
++		VNC_DEBUG("Initializing VNC server with TLS SASL auth\n");
++                vs->subauth = VNC_AUTH_VENCRYPT_TLSSASL;
++            }
++        } else {
++#endif /* CONFIG_VNC_TLS */
++	    VNC_DEBUG("Initializing VNC server with SASL auth\n");
++            vs->auth = VNC_AUTH_SASL;
++#ifdef CONFIG_VNC_TLS
++            vs->subauth = VNC_AUTH_INVALID;
++        }
++#endif /* CONFIG_VNC_TLS */
++#endif /* CONFIG_VNC_SASL */
+     } else {
+ #ifdef CONFIG_VNC_TLS
+ 	if (tls) {
+@@ -2018,6 +2203,16 @@ int vnc_display_open(DisplayState *ds, c
+ #endif
+     }
+ 
++#ifdef CONFIG_VNC_SASL
++    if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) {
++        fprintf(stderr, "Failed to initialize SASL auth %s",
++                sasl_errstring(saslErr, NULL, NULL));
++        free(vs->display);
++        vs->display = NULL;
++        return -1;
++    }
++#endif
++
+     if (reverse) {
+         /* connect to viewer */
+         if (strncmp(display, "unix:", 5) == 0)
+Index: qemu-kvm-0.10/qemu/vnc.h
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/vnc.h
++++ qemu-kvm-0.10/qemu/vnc.h
+@@ -79,6 +79,10 @@ typedef struct VncDisplay VncDisplay;
+ #include "vnc-tls.h"
+ #include "vnc-auth-vencrypt.h"
+ #endif
++#ifdef CONFIG_VNC_SASL
++#include "vnc-auth-sasl.h"
++#endif
++
+ 
+ struct VncDisplay
+ {
+@@ -118,10 +122,12 @@ struct VncState
+     int minor;
+ 
+     char challenge[VNC_AUTH_CHALLENGE_SIZE];
+-
+ #ifdef CONFIG_VNC_TLS
+     VncStateTLS tls;
+ #endif
++#ifdef CONFIG_VNC_SASL
++    VncStateSASL sasl;
++#endif
+ 
+     Buffer output;
+     Buffer input;
+@@ -160,8 +166,9 @@ enum {
+     VNC_AUTH_RA2NE = 6,
+     VNC_AUTH_TIGHT = 16,
+     VNC_AUTH_ULTRA = 17,
+-    VNC_AUTH_TLS = 18,
+-    VNC_AUTH_VENCRYPT = 19
++    VNC_AUTH_TLS = 18,      /* Supported in GTK-VNC & VINO */
++    VNC_AUTH_VENCRYPT = 19, /* Supported in GTK-VNC & VeNCrypt */
++    VNC_AUTH_SASL = 20,     /* Supported in GTK-VNC & VINO */
+ };
+ 
+ enum {
+@@ -172,6 +179,8 @@ enum {
+     VNC_AUTH_VENCRYPT_X509NONE = 260,
+     VNC_AUTH_VENCRYPT_X509VNC = 261,
+     VNC_AUTH_VENCRYPT_X509PLAIN = 262,
++    VNC_AUTH_VENCRYPT_X509SASL = 263,
++    VNC_AUTH_VENCRYPT_TLSSASL = 264,
+ };
+ 
+ 
+@@ -255,6 +264,8 @@ enum {
+ void vnc_client_read(void *opaque);
+ void vnc_client_write(void *opaque);
+ 
++long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen);
++long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen);
+ 
+ /* Protocol I/O functions */
+ void vnc_write(VncState *vs, const void *data, size_t len);
+@@ -274,8 +285,22 @@ uint32_t read_u32(uint8_t *data, size_t 
+ 
+ /* Protocol stage functions */
+ void vnc_client_error(VncState *vs);
++int vnc_client_io_error(VncState *vs, int ret, int last_errno);
+ 
+ void start_client_init(VncState *vs);
+ void start_auth_vnc(VncState *vs);
+ 
++/* Buffer management */
++void buffer_reserve(Buffer *buffer, size_t len);
++int buffer_empty(Buffer *buffer);
++uint8_t *buffer_end(Buffer *buffer);
++void buffer_reset(Buffer *buffer);
++void buffer_append(Buffer *buffer, const void *data, size_t len);
++
++
++/* Misc helpers */
++
++char *vnc_socket_local_addr(const char *format, int fd);
++char *vnc_socket_remote_addr(const char *format, int fd);
++
+ #endif /* __QEMU_VNC_H */
diff --git a/07-vnc-monitor-authinfo.patch b/07-vnc-monitor-authinfo.patch
new file mode 100644
index 0000000..f691c7e
--- /dev/null
+++ b/07-vnc-monitor-authinfo.patch
@@ -0,0 +1,93 @@
+Index: qemu-kvm-0.10/qemu/vnc-tls.c
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/vnc-tls.c
++++ qemu-kvm-0.10/qemu/vnc-tls.c
+@@ -241,6 +241,22 @@ int vnc_tls_validate_certificate(struct 
+ 	    return -1;
+ 	}
+ 
++	if (i == 0) {
++	    size_t dnameSize = 1024;
++	    vs->tls.dname = qemu_malloc(dnameSize);
++	requery:
++	    if ((ret = gnutls_x509_crt_get_dn (cert, vs->tls.dname, &dnameSize)) != 0) {
++		if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
++		    vs->tls.dname = qemu_realloc(vs->tls.dname, dnameSize);
++		    goto requery;
++		}
++		gnutls_x509_crt_deinit (cert);
++		VNC_DEBUG("Cannot get client distinguished name: %s",
++			  gnutls_strerror (ret));
++		return -1;
++	    }
++	}
++
+ 	gnutls_x509_crt_deinit (cert);
+     }
+ 
+@@ -347,6 +363,7 @@ void vnc_tls_client_cleanup(struct VncSt
+ 	vs->tls.session = NULL;
+     }
+     vs->tls.wiremode = VNC_WIREMODE_CLEAR;
++    free(vs->tls.dname);
+ }
+ 
+ 
+Index: qemu-kvm-0.10/qemu/vnc-tls.h
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/vnc-tls.h
++++ qemu-kvm-0.10/qemu/vnc-tls.h
+@@ -55,6 +55,9 @@ struct VncStateTLS {
+     /* Whether data is being TLS encrypted yet */
+     int wiremode;
+     gnutls_session_t session;
++
++    /* Client's Distinguished Name from the x509 cert */
++    char *dname;
+ };
+ 
+ int vnc_tls_client_setup(VncState *vs, int x509Creds);
+Index: qemu-kvm-0.10/qemu/vnc.c
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/vnc.c
++++ qemu-kvm-0.10/qemu/vnc.c
+@@ -156,6 +156,21 @@ static void do_info_vnc_client(VncState 
+     term_puts("Client:\n");
+     term_puts(clientAddr);
+     free(clientAddr);
++
++#ifdef CONFIG_VNC_TLS
++    if (client->tls.session &&
++	client->tls.dname)
++	term_printf("  x509 dname: %s\n", client->tls.dname);
++    else
++	term_puts("  x509 dname: none\n");
++#endif
++#ifdef CONFIG_VNC_SASL
++    if (client->sasl.conn &&
++	client->sasl.username)
++	term_printf("    username: %s\n", client->sasl.username);
++    else
++	term_puts("    username: none\n");
++#endif
+ }
+ 
+ void do_info_vnc(void)
+@@ -1823,7 +1838,7 @@ static int protocol_client_auth(VncState
+     /* We only advertise 1 auth scheme at a time, so client
+      * must pick the one we sent. Verify this */
+     if (data[0] != vs->vd->auth) { /* Reject auth */
+-       VNC_DEBUG("Reject auth %d\n", (int)data[0]);
++       VNC_DEBUG("Reject auth %d because it didn't match advertized\n", (int)data[0]);
+        vnc_write_u32(vs, 1);
+        if (vs->minor >= 8) {
+            static const char err[] = "Authentication failed";
+@@ -1863,7 +1878,7 @@ static int protocol_client_auth(VncState
+ #endif /* CONFIG_VNC_SASL */
+ 
+        default: /* Should not be possible, but just in case */
+-           VNC_DEBUG("Reject auth %d\n", vs->vd->auth);
++           VNC_DEBUG("Reject auth %d server code bug\n", vs->vd->auth);
+            vnc_write_u8(vs, 1);
+            if (vs->minor >= 8) {
+                static const char err[] = "Authentication failed";
diff --git a/08-vnc-acl-mgmt.patch b/08-vnc-acl-mgmt.patch
new file mode 100644
index 0000000..a4ae11d
--- /dev/null
+++ b/08-vnc-acl-mgmt.patch
@@ -0,0 +1,709 @@
+Index: qemu-kvm-0.10/qemu/Makefile
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/Makefile
++++ qemu-kvm-0.10/qemu/Makefile
+@@ -148,7 +148,7 @@ endif
+ ifdef CONFIG_CURSES
+ OBJS+=curses.o
+ endif
+-OBJS+=vnc.o d3des.o
++OBJS+=vnc.o acl.o d3des.o
+ ifdef CONFIG_VNC_TLS
+ OBJS+=vnc-tls.o vnc-auth-vencrypt.o
+ endif
+@@ -178,9 +178,11 @@ sdl.o: sdl.c keymaps.h sdl_keysym.h
+ 
+ sdl.o audio/sdlaudio.o: CFLAGS += $(SDL_CFLAGS)
+ 
++acl.o: acl.h acl.c
++
+ vnc.h: vnc-tls.h vnc-auth-vencrypt.h vnc-auth-sasl.h keymaps.h
+ 
+-vnc.o: vnc.c vnc.h vnc_keysym.h vnchextile.h d3des.c d3des.h
++vnc.o: vnc.c vnc.h vnc_keysym.h vnchextile.h d3des.c d3des.h acl.h
+ 
+ vnc.o: CFLAGS += $(CONFIG_VNC_TLS_CFLAGS)
+ 
+Index: qemu-kvm-0.10/qemu/acl.c
+===================================================================
+--- /dev/null
++++ qemu-kvm-0.10/qemu/acl.c
+@@ -0,0 +1,185 @@
++/*
++ * QEMU access control list management
++ *
++ * 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 "qemu-common.h"
++#include "sysemu.h"
++#include "acl.h"
++
++#ifdef HAVE_FNMATCH_H
++#include <fnmatch.h>
++#endif
++
++
++static unsigned int nacls = 0;
++static qemu_acl **acls = NULL;
++
++
++
++qemu_acl *qemu_acl_find(const char *aclname)
++{
++    int i;
++    for (i = 0 ; i < nacls ; i++) {
++	if (strcmp(acls[i]->aclname, aclname) == 0)
++	    return acls[i];
++    }
++
++    return NULL;
++}
++
++qemu_acl *qemu_acl_init(const char *aclname)
++{
++    qemu_acl *acl;
++
++    acl = qemu_acl_find(aclname);
++    if (acl)
++	return acl;
++
++    acl = qemu_malloc(sizeof(*acl));
++    acl->aclname = qemu_strdup(aclname);
++    /* Deny by default, so there is no window of "open
++     * access" between QEMU starting, and the user setting
++     * up ACLs in the monitor */
++    acl->defaultDeny = 1;
++
++    acl->nentries = 0;
++    TAILQ_INIT(&acl->entries);
++
++    acls = qemu_realloc(acls, sizeof(*acls) * (nacls +1));
++    acls[nacls] = acl;
++    nacls++;
++
++    return acl;
++}
++
++int qemu_acl_party_is_allowed(qemu_acl *acl,
++			      const char *party)
++{
++    qemu_acl_entry *entry;
++
++    TAILQ_FOREACH(entry, &acl->entries, next) {
++#ifdef HAVE_FNMATCH_H
++	if (fnmatch(entry->match, party, 0) == 0)
++	    return entry->deny ? 0 : 1;
++#else
++	/* No fnmatch, so fallback to exact string matching
++	 * instead of allowing wildcards */
++	if (strcmp(entry->match, party) == 0)
++	    return entry->deny ? 0 : 1;
++#endif
++    }
++
++    return acl->defaultDeny ? 0 : 1;
++}
++
++
++void qemu_acl_reset(qemu_acl *acl)
++{
++    qemu_acl_entry *entry;
++
++    /* Put back to deny by default, so there is no window
++     * of "open access" while the user re-initializes the
++     * access control list */
++    acl->defaultDeny = 1;
++    TAILQ_FOREACH(entry, &acl->entries, next) {
++	TAILQ_REMOVE(&acl->entries, entry, next);
++	free(entry->match);
++	free(entry);
++    }
++    acl->nentries = 0;
++}
++
++
++int qemu_acl_append(qemu_acl *acl,
++		    int deny,
++		    const char *match)
++{
++    qemu_acl_entry *entry;
++
++    entry = qemu_malloc(sizeof(*entry));
++    entry->match = qemu_strdup(match);
++    entry->deny = deny;
++
++    TAILQ_INSERT_TAIL(&acl->entries, entry, next);
++    acl->nentries++;
++
++    return acl->nentries;
++}
++
++
++int qemu_acl_insert(qemu_acl *acl,
++		    int deny,
++		    const char *match,
++		    int index)
++{
++    qemu_acl_entry *entry;
++    qemu_acl_entry *tmp;
++    int i = 0;
++
++    if (index <= 0)
++	return -1;
++    if (index >= acl->nentries)
++	return qemu_acl_append(acl, deny, match);
++
++
++    entry = qemu_malloc(sizeof(*entry));
++    entry->match = qemu_strdup(match);
++    entry->deny = deny;
++
++    TAILQ_FOREACH(tmp, &acl->entries, next) {
++	i++;
++	if (i == index) {
++	    TAILQ_INSERT_BEFORE(tmp, entry, next);
++	    acl->nentries++;
++	    break;
++	}
++    }
++
++    return i;
++}
++
++int qemu_acl_remove(qemu_acl *acl,
++		    const char *match)
++{
++    qemu_acl_entry *entry;
++    int i = 0;
++
++    TAILQ_FOREACH(entry, &acl->entries, next) {
++	i++;
++	if (strcmp(entry->match, match) == 0) {
++	    TAILQ_REMOVE(&acl->entries, entry, next);
++	    return i;
++	}
++    }
++    return -1;
++}
++
++
++/*
++ * Local variables:
++ *  c-indent-level: 4
++ *  c-basic-offset: 4
++ *  tab-width: 8
++ * End:
++ */
+Index: qemu-kvm-0.10/qemu/acl.h
+===================================================================
+--- /dev/null
++++ qemu-kvm-0.10/qemu/acl.h
+@@ -0,0 +1,74 @@
++/*
++ * QEMU access control list management
++ *
++ * 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_ACL_H__
++#define __QEMU_ACL_H__
++
++#include "sys-queue.h"
++
++typedef struct qemu_acl_entry qemu_acl_entry;
++typedef struct qemu_acl qemu_acl;
++
++struct qemu_acl_entry {
++    char *match;
++    int deny;
++
++    TAILQ_ENTRY(qemu_acl_entry) next;
++};
++
++struct qemu_acl {
++    char *aclname;
++    unsigned int nentries;
++    TAILQ_HEAD(,qemu_acl_entry) entries;
++    int defaultDeny;
++};
++
++qemu_acl *qemu_acl_init(const char *aclname);
++
++qemu_acl *qemu_acl_find(const char *aclname);
++
++int qemu_acl_party_is_allowed(qemu_acl *acl,
++			      const char *party);
++
++void qemu_acl_reset(qemu_acl *acl);
++
++int qemu_acl_append(qemu_acl *acl,
++		    int deny,
++		    const char *match);
++int qemu_acl_insert(qemu_acl *acl,
++		    int deny,
++		    const char *match,
++		    int index);
++int qemu_acl_remove(qemu_acl *acl,
++		    const char *match);
++
++#endif /* __QEMU_ACL_H__ */
++
++/*
++ * Local variables:
++ *  c-indent-level: 4
++ *  c-basic-offset: 4
++ *  tab-width: 8
++ * End:
++ */
+Index: qemu-kvm-0.10/qemu/configure
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/configure
++++ qemu-kvm-0.10/qemu/configure
+@@ -913,6 +913,21 @@ EOF
+ fi
+ 
+ ##########################################
++# fnmatch() probe, used for ACL routines
++fnmatch="no"
++cat > $TMPC << EOF
++#include <fnmatch.h>
++int main(void)
++{
++    fnmatch("foo", "foo", 0);
++    return 0;
++}
++EOF
++if $cc $ARCH_CFLAGS -o $TMPE $TMPC > /dev/null 2> /dev/null ; then
++   fnmatch="yes"
++fi
++
++##########################################
+ # vde libraries probe
+ if test "$vde" = "yes" ; then
+   cat > $TMPC << EOF
+@@ -1501,6 +1516,9 @@ if test "$vnc_sasl" = "yes" ; then
+   echo "CONFIG_VNC_SASL_LIBS=$vnc_sasl_libs" >> $config_mak
+   echo "#define CONFIG_VNC_SASL 1" >> $config_h
+ fi
++if test "$fnmatch" = "yes" ; then
++  echo "#define HAVE_FNMATCH_H 1" >> $config_h
++fi
+ qemu_version=`head $source_path/VERSION`
+ echo "VERSION=$qemu_version" >>$config_mak
+ echo "#define QEMU_VERSION \"$qemu_version\"" >> $config_h
+Index: qemu-kvm-0.10/qemu/monitor.c
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/monitor.c
++++ qemu-kvm-0.10/qemu/monitor.c
+@@ -39,6 +39,7 @@
+ #include "qemu-timer.h"
+ #include "migration.h"
+ #include "kvm.h"
++#include "acl.h"
+ 
+ #include "qemu-kvm.h"
+ 
+@@ -1498,6 +1499,85 @@ static void do_info_balloon(void)
+         term_printf("balloon: actual=%d\n", (int)(actual >> 20));
+ }
+ 
++static void do_acl(const char *command,
++		   const char *aclname,
++		   const char *match,
++		   int has_index,
++		   int index)
++{
++    qemu_acl *acl;
++
++    acl = qemu_acl_find(aclname);
++    if (!acl) {
++	term_printf("acl: unknown list '%s'\n", aclname);
++	return;
++    }
++
++    if (strcmp(command, "show") == 0) {
++	int i = 0;
++	qemu_acl_entry *entry;
++	term_printf("policy: %s\n",
++		    acl->defaultDeny ? "deny" : "allow");
++	TAILQ_FOREACH(entry, &acl->entries, next) {
++	    i++;
++	    term_printf("%d: %s %s\n", i,
++			entry->deny ? "deny" : "allow",
++			entry->match);
++	}
++    } else if (strcmp(command, "reset") == 0) {
++	qemu_acl_reset(acl);
++	term_printf("acl: removed all rules\n");
++    } else if (strcmp(command, "policy") == 0) {
++	if (!match) {
++	    term_printf("acl: missing policy parameter\n");
++	    return;
++	}
++
++	if (strcmp(match, "allow") == 0) {
++	    acl->defaultDeny = 0;
++	    term_printf("acl: policy set to 'allow'\n");
++	} else if (strcmp(match, "deny") == 0) {
++	    acl->defaultDeny = 1;
++	    term_printf("acl: policy set to 'deny'\n");
++	} else {
++	    term_printf("acl: unknown policy '%s', expected 'deny' or 'allow'\n", match);
++	}
++    } else if ((strcmp(command, "allow") == 0) ||
++	       (strcmp(command, "deny") == 0)) {
++	int deny = strcmp(command, "deny") == 0 ? 1 : 0;
++	int ret;
++
++	if (!match) {
++	    term_printf("acl: missing match parameter\n");
++	    return;
++	}
++
++	if (has_index)
++	    ret = qemu_acl_insert(acl, deny, match, index);
++	else
++	    ret = qemu_acl_append(acl, deny, match);
++	if (ret < 0)
++	    term_printf("acl: unable to add acl entry\n");
++	else
++	    term_printf("acl: added rule at position %d\n", ret);
++    } else if (strcmp(command, "remove") == 0) {
++	int ret;
++
++	if (!match) {
++	    term_printf("acl: missing match parameter\n");
++	    return;
++	}
++
++	ret = qemu_acl_remove(acl, match);
++	if (ret < 0)
++	    term_printf("acl: no matching acl entry\n");
++	else
++	    term_printf("acl: removed rule at position %d\n", ret);
++    } else {
++	term_printf("acl: unknown command '%s'\n", command);
++    }
++}
++
+ /* Please update qemu-doc.texi when adding or changing commands */
+ static const term_cmd_t term_cmds[] = {
+     { "help|?", "s?", do_help,
+@@ -1603,6 +1683,12 @@ static const term_cmd_t term_cmds[] = {
+     { "set_link", "ss", do_set_link,
+       "name [up|down]", "change the link status of a network adapter" },
+     { "set_link", "ss", do_set_link, "name [up|down]" },
++    { "acl", "sss?i?", do_acl, "<command> <aclname> [<match>] [<index>]\n",
++                                "acl show vnc.username\n"
++                                "acl policy vnc.username deny\n"
++                                "acl allow vnc.username fred\n"
++                                "acl deny vnc.username bob\n"
++                                "acl reset vnc.username\n" },
+     { "cpu_set", "is", do_cpu_set_nr, "cpu [online|offline]", "change cpu state" },
+ #if defined(TARGET_I386) || defined(TARGET_X86_64)
+     { "drive_add", "iss", drive_hot_add, "pcibus pcidevfn [file=file][,if=type][,bus=n]\n"
+@@ -1611,6 +1697,7 @@ static const term_cmd_t term_cmds[] = {
+                                         "[snapshot=on|off][,cache=on|off]",
+                                         "add drive to PCI storage controller" },
+ #endif
++
+     { NULL, NULL, },
+ };
+ 
+@@ -2995,3 +3082,12 @@ int monitor_read_bdrv_key(BlockDriverSta
+     }
+     return -EPERM;
+ }
++
++
++/*
++ * Local variables:
++ *  c-indent-level: 4
++ *  c-basic-offset: 4
++ *  tab-width: 8
++ * End:
++ */
+Index: qemu-kvm-0.10/qemu/qemu-doc.texi
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/qemu-doc.texi
++++ qemu-kvm-0.10/qemu/qemu-doc.texi
+@@ -639,6 +639,19 @@ ensures a data encryption preventing com
+ credentials. See the @ref{vnc_security} section for details on using
+ SASL authentication.
+ 
++@item acl
++
++Turn on access control lists for checking of the x509 client certificate
++and SASL party. For x509 certs, the ACL check is made against the
++certificate's distinguished name. This is something that looks like
++@code{C=GB,O=ACME,L=Boston,CN=bob}. For SASL party, the ACL check is
++made against the username, which depending on the SASL plugin, may
++include a realm component, eg @code{bob} or @code{bob\@EXAMPLE.COM}.
++When the @option{acl} flag is set, the initial access list will be
++empty, with a @code{deny} policy. Thus no one will be allowed to
++use the VNC server until the ACLs have been loaded. This can be
++achieved using the @code{acl} monitor command.
++
+ @end table
+ 
+ @end table
+@@ -1400,6 +1413,42 @@ Password: ********
+ 
+ @end table
+ 
++@item acl @var{subcommand} @var{aclname} @var{match} @var{index}
++
++Manage access control lists for network services. There are currently
++two named access control lists, @var{vnc.x509dname} and @var{vnc.username}
++matching on the x509 client certificate distinguished name, and SASL
++username respectively.
++
++@table @option
++@item acl show <aclname>
++list all the match rules in the access control list, and the default
++policy
++@item acl policy <aclname> @code{allow|deny}
++set the default access control list policy, used in the event that
++none of the explicit rules match. The default policy at startup is
++always @code{deny}
++@item acl allow <aclname> <match> [<index>]
++add a match to the access control list, allowing access. The match will
++normally be an exact username or x509 distinguished name, but can
++optionally include wildcard globs. eg @code{*\@EXAMPLE.COM} to allow
++all users in the @code{EXAMPLE.COM} kerberos realm. The match will
++normally be appended to the end of the ACL, but can be inserted
++earlier in the list if the optional @code{index} parameter is supplied.
++@item acl deny <aclname> <match> [<index>]
++add a match to the access control list, denying access. The match will
++normally be an exact username or x509 distinguished name, but can
++optionally include wildcard globs. eg @code{*\@EXAMPLE.COM} to allow
++all users in the @code{EXAMPLE.COM} kerberos realm. The match will
++normally be appended to the end of the ACL, but can be inserted
++earlier in the list if the optional @code{index} parameter is supplied.
++@item acl remove <aclname> <match>
++remove the specified match rule from the access control list.
++@item acl reset <aclname>
++remove all matches from the access control list, and set the default
++policy back to @code{deny}.
++@end table
++
+ @item screendump @var{filename}
+ Save screen into PPM image @var{filename}.
+ 
+Index: qemu-kvm-0.10/qemu/vnc-auth-sasl.c
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/vnc-auth-sasl.c
++++ qemu-kvm-0.10/qemu/vnc-auth-sasl.c
+@@ -120,22 +120,32 @@ static int vnc_auth_sasl_check_access(Vn
+ {
+     const void *val;
+     int err;
++    int allow;
+ 
+     err = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val);
+     if (err != SASL_OK) {
+-	VNC_DEBUG("cannot query SASL username on connection %d (%s)\n",
++	VNC_DEBUG("cannot query SASL username on connection %d (%s), denying access\n",
+ 		  err, sasl_errstring(err, NULL, NULL));
+ 	return -1;
+     }
+     if (val == NULL) {
+-	VNC_DEBUG("no client username was found\n");
++	VNC_DEBUG("no client username was found, denying access\n");
+ 	return -1;
+     }
+     VNC_DEBUG("SASL client username %s\n", (const char *)val);
+ 
+     vs->sasl.username = qemu_strdup((const char*)val);
+ 
+-    return 0;
++    if (vs->vd->sasl.acl == NULL) {
++	VNC_DEBUG("no ACL activated, allowing access\n");
++	return 0;
++    }
++
++    allow = qemu_acl_party_is_allowed(vs->vd->sasl.acl, vs->sasl.username);
++
++    VNC_DEBUG("SASL client %s %s by ACL\n", vs->sasl.username,
++	      allow ? "allowed" : "denied");
++    return allow ? 0 : -1;
+ }
+ 
+ static int vnc_auth_sasl_check_ssf(VncState *vs)
+Index: qemu-kvm-0.10/qemu/vnc-auth-sasl.h
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/vnc-auth-sasl.h
++++ qemu-kvm-0.10/qemu/vnc-auth-sasl.h
+@@ -30,6 +30,9 @@
+ #include <sasl/sasl.h>
+ 
+ typedef struct VncStateSASL VncStateSASL;
++typedef struct VncDisplaySASL VncDisplaySASL;
++
++#include "acl.h"
+ 
+ struct VncStateSASL {
+     sasl_conn_t *conn;
+@@ -56,6 +59,10 @@ struct VncStateSASL {
+     char *mechlist;
+ };
+ 
++struct VncDisplaySASL {
++    qemu_acl *acl;
++};
++
+ void vnc_sasl_client_cleanup(VncState *vs);
+ 
+ long vnc_client_read_sasl(VncState *vs);
+Index: qemu-kvm-0.10/qemu/vnc-tls.c
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/vnc-tls.c
++++ qemu-kvm-0.10/qemu/vnc-tls.c
+@@ -255,6 +255,25 @@ int vnc_tls_validate_certificate(struct 
+ 			  gnutls_strerror (ret));
+ 		return -1;
+ 	    }
++
++	    if (vs->vd->tls.x509verify) {
++		int allow;
++		if (!vs->vd->tls.acl) {
++		    VNC_DEBUG("no ACL activated, allowing access");
++		    gnutls_x509_crt_deinit (cert);
++		    continue;
++		}
++
++		allow = qemu_acl_party_is_allowed(vs->vd->tls.acl,
++						  vs->tls.dname);
++
++		VNC_DEBUG("TLS x509 ACL check for %s is %s\n",
++			  vs->tls.dname, allow ? "allowed" : "denied");
++		if (!allow) {
++		    gnutls_x509_crt_deinit (cert);
++		    return -1;
++		}
++	    }
+ 	}
+ 
+ 	gnutls_x509_crt_deinit (cert);
+Index: qemu-kvm-0.10/qemu/vnc-tls.h
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/vnc-tls.h
++++ qemu-kvm-0.10/qemu/vnc-tls.h
+@@ -31,6 +31,8 @@
+ #include <gnutls/gnutls.h>
+ #include <gnutls/x509.h>
+ 
++#include "acl.h"
++
+ enum {
+     VNC_WIREMODE_CLEAR,
+     VNC_WIREMODE_TLS,
+@@ -42,6 +44,7 @@ typedef struct VncStateTLS VncStateTLS;
+ /* Server state */
+ struct VncDisplayTLS {
+     int x509verify; /* Non-zero if server requests & validates client cert */
++    qemu_acl *acl;
+ 
+     /* Paths to x509 certs/keys */
+     char *x509cacert;
+Index: qemu-kvm-0.10/qemu/vnc.c
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/vnc.c
++++ qemu-kvm-0.10/qemu/vnc.c
+@@ -28,6 +28,7 @@
+ #include "sysemu.h"
+ #include "qemu_socket.h"
+ #include "qemu-timer.h"
++#include "acl.h"
+ 
+ #define VNC_REFRESH_INTERVAL (1000 / 30)
+ 
+@@ -2082,6 +2083,7 @@ int vnc_display_open(DisplayState *ds, c
+     int sasl = 0;
+     int saslErr;
+ #endif
++    int acl = 0;
+ 
+     if (!vnc_display)
+         return -1;
+@@ -2138,9 +2140,28 @@ int vnc_display_open(DisplayState *ds, c
+ 		return -1;
+ 	    }
+ #endif
++	} else if (strncmp(options, "acl", 3) == 0) {
++	    acl = 1;
+ 	}
+     }
+ 
++#ifdef CONFIG_VNC_TLS
++    if (acl && x509 && vs->tls.x509verify) {
++	if (!(vs->tls.acl = qemu_acl_init("vnc.x509dname"))) {
++	    fprintf(stderr, "Failed to create x509 dname ACL\n");
++	    exit(1);
++	}
++    }
++#endif
++#ifdef CONFIG_VNC_SASL
++    if (acl && sasl) {
++	if (!(vs->sasl.acl = qemu_acl_init("vnc.username"))) {
++	    fprintf(stderr, "Failed to create username ACL\n");
++	    exit(1);
++	}
++    }
++#endif
++
+     /*
+      * Combinations we support here:
+      *
+Index: qemu-kvm-0.10/qemu/vnc.h
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/vnc.h
++++ qemu-kvm-0.10/qemu/vnc.h
+@@ -98,6 +98,9 @@ struct VncDisplay
+     int subauth; /* Used by VeNCrypt */
+     VncDisplayTLS tls;
+ #endif
++#ifdef CONFIG_VNC_SASL
++    VncDisplaySASL sasl;
++#endif
+ };
+ 
+ struct VncState
diff --git a/kvm-upstream-ppc.patch b/kvm-upstream-ppc.patch
index 5d1ca4c..01852a9 100644
--- a/kvm-upstream-ppc.patch
+++ b/kvm-upstream-ppc.patch
@@ -18,10 +18,10 @@ Signed-off-by: Glauber Costa <glommer@redhat.com>
  target-ppc/helper.c    |    2 +-
  4 files changed, 4 insertions(+), 1 deletion(-)
 
-diff --git a/qemu/hw/ppc440.c b/qemu/hw/ppc440.c
-index 00d82e4..164c326 100644
---- a/qemu/hw/ppc440.c
-+++ b/qemu/hw/ppc440.c
+Index: qemu-kvm-0.10/qemu/hw/ppc440.c
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/hw/ppc440.c
++++ qemu-kvm-0.10/qemu/hw/ppc440.c
 @@ -18,6 +18,7 @@
  #include "ppc440.h"
  #include "ppc405.h"
@@ -30,10 +30,10 @@ index 00d82e4..164c326 100644
  #include "kvm.h"
  
  #define PPC440EP_PCI_CONFIG     0xeec00000
-diff --git a/qemu/hw/ppc440_bamboo.c b/qemu/hw/ppc440_bamboo.c
-index fbd447c..60ddaf4 100644
---- a/qemu/hw/ppc440_bamboo.c
-+++ b/qemu/hw/ppc440_bamboo.c
+Index: qemu-kvm-0.10/qemu/hw/ppc440_bamboo.c
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/hw/ppc440_bamboo.c
++++ qemu-kvm-0.10/qemu/hw/ppc440_bamboo.c
 @@ -21,6 +21,7 @@
  #include "boards.h"
  #include "sysemu.h"
@@ -42,10 +42,10 @@ index fbd447c..60ddaf4 100644
  #include "kvm.h"
  #include "kvm_ppc.h"
  #include "device_tree.h"
-diff --git a/qemu/hw/ppce500_mpc8544ds.c b/qemu/hw/ppce500_mpc8544ds.c
-index 3d73f8a..d8ea947 100644
---- a/qemu/hw/ppce500_mpc8544ds.c
-+++ b/qemu/hw/ppce500_mpc8544ds.c
+Index: qemu-kvm-0.10/qemu/hw/ppce500_mpc8544ds.c
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/hw/ppce500_mpc8544ds.c
++++ qemu-kvm-0.10/qemu/hw/ppce500_mpc8544ds.c
 @@ -16,6 +16,7 @@
  
  #include <dirent.h>
@@ -54,10 +54,10 @@ index 3d73f8a..d8ea947 100644
  #include "config.h"
  #include "qemu-common.h"
  #include "net.h"
-diff --git a/qemu/target-ppc/helper.c b/qemu/target-ppc/helper.c
-index 80b53eb..e8dc384 100644
---- a/qemu/target-ppc/helper.c
-+++ b/qemu/target-ppc/helper.c
+Index: qemu-kvm-0.10/qemu/target-ppc/helper.c
+===================================================================
+--- qemu-kvm-0.10.orig/qemu/target-ppc/helper.c
++++ qemu-kvm-0.10/qemu/target-ppc/helper.c
 @@ -29,6 +29,7 @@
  #include "exec-all.h"
  #include "helper_regs.h"
@@ -66,6 +66,3 @@ index 80b53eb..e8dc384 100644
  #include "kvm.h"
  
  //#define DEBUG_MMU
--- 
-1.6.1.2
-
diff --git a/qemu.spec b/qemu.spec
index 1f6b432..1878286 100644
--- a/qemu.spec
+++ b/qemu.spec
@@ -1,7 +1,7 @@
 Summary: QEMU is a FAST! processor emulator
 Name: qemu
 Version: 0.10
-Release: 0.12.kvm20090323git%{?dist}
+Release: 1%{?dist}
 # I have mistakenly thought the revision name would be 1.0.
 # So 0.10 series get Epoch = 1
 Epoch: 2
@@ -16,11 +16,23 @@ URL: http://www.qemu.org/
 # mkdir $(HOME)/sf-releases
 # ./scripts/make-release kvm-85rc-1.git-snapshot-date +%Y%m%d HEAD HEAD
 
-Source0: qemu-kvm-devel-85rc1.git-snapshot-20090323.tar.gz
+Source0: qemu-kvm-%{version}.tar.gz
 Source1: qemu.init
 Source2: kvm.modules
 
-Patch1: kvm-upstream-ppc.patch
+
+Patch1: 01-tls-handshake-fix.patch
+Patch2: 02-vnc-monitor-info.patch
+Patch3: 03-display-keymaps.patch
+Patch4: 04-vnc-struct.patch
+Patch5: 05-vnc-tls-vencrypt.patch
+Patch6: 06-vnc-sasl.patch
+Patch7: 07-vnc-monitor-authinfo.patch
+Patch8: 08-vnc-acl-mgmt.patch
+
+Patch9: kvm-upstream-ppc.patch
+Patch10: qemu-fix-debuginfo.patch
+Patch11: qemu-fix-gcc.patch
 
 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 BuildRequires: SDL-devel zlib-devel which texi2html gnutls-devel cyrus-sasl-devel
@@ -55,7 +67,6 @@ As QEMU requires no host kernel patches to run, it is safe and easy to use.
 %package  img
 Summary: QEMU command line tool for manipulating disk images
 Group: Development/Tools
-Requires: %{name}-common = %{epoch}:%{version}-%{release}
 %description img
 QEMU is a generic and open source processor emulator which achieves a good 
 emulation speed by using dynamic translation.
@@ -106,6 +117,7 @@ platform.
 Summary: QEMU system emulator for ppc
 Group: Development/Tools
 Requires: %{name}-common = %{epoch}:%{version}-%{release}
+Requires: openbios-ppc
 %description system-ppc
 QEMU is a generic and open source processor emulator which achieves a good
 emulation speed by using dynamic translation.
@@ -183,8 +195,19 @@ such as kvmtrace and kvm_stat.
 %endif
 
 %prep
-%setup -q -n qemu-kvm-devel-85rc1.git-snapshot-20090323
+%setup -q -n qemu-kvm-%{version}
+
 %patch1 -p1
+%patch2 -p1
+%patch3 -p1
+%patch4 -p1
+%patch5 -p1
+%patch6 -p1
+%patch7 -p1
+%patch8 -p1
+%patch9 -p1
+%patch10 -p1
+%patch11 -p1
 
 %build
 # systems like rhel build system does not have a recent enough linker so
@@ -212,6 +235,8 @@ echo "%{name}-%{version}" > $(pwd)/kernel/.kernelrelease
 ./configure --target-list=x86_64-softmmu \
             --kerneldir=$(pwd)/kernel --prefix=%{_prefix} \
             --audio-drv-list=sdl,alsa,oss \
+            --with-patched-kernel \
+            --disable-strip \
             --qemu-ldflags=$extraldflags \
             --qemu-cflags="$RPM_OPT_FLAGS"
 
@@ -236,6 +261,7 @@ cd qemu
     --prefix=%{_prefix} \
     --interp-prefix=%{_prefix}/qemu-%%M \
     --kerneldir=$(pwd)/../kernel --prefix=%{_prefix} \
+    --disable-strip \
     --disable-kvm \
     --extra-ldflags=$extraldflags \
     --audio-drv-list=sdl,alsa,oss \
@@ -275,6 +301,9 @@ install -D -p -m 0644 qemu.sasl $RPM_BUILD_ROOT%{_sysconfdir}/sasl2/qemu.conf
 rm -rf ${RPM_BUILD_ROOT}/usr/share//qemu/pxe*bin
 rm -rf ${RPM_BUILD_ROOT}/usr/share//qemu/vgabios*bin
 rm -rf ${RPM_BUILD_ROOT}/usr/share//qemu/bios.bin
+rm -rf ${RPM_BUILD_ROOT}/usr/share//qemu/openbios-ppc
+rm -rf ${RPM_BUILD_ROOT}/usr/share//qemu/openbios-sparc32
+rm -rf ${RPM_BUILD_ROOT}/usr/share//qemu/openbios-sparc64
 
 # the pxe etherboot images will be symlinks to the images on
 # /usr/share/etherboot, as QEMU doesn't know how to look
@@ -291,6 +320,9 @@ pxe_link virtio virtio-net
 ln -s ../vgabios/VGABIOS-lgpl-latest.bin  %{buildroot}/%{_prefix}/share/qemu/vgabios.bin
 ln -s ../vgabios/VGABIOS-lgpl-latest.cirrus.bin %{buildroot}/%{_prefix}/share/qemu/vgabios-cirrus.bin
 ln -s ../bochs/BIOS-bochs-kvm %{buildroot}/%{_prefix}/share/qemu/bios.bin
+ln -s ../openbios/openbios-ppc %{buildroot}/%{_prefix}/share/qemu/openbios-ppc
+ln -s ../openbios/openbios-sparc32 %{buildroot}/%{_prefix}/share/qemu/openbios-sparc32
+ln -s ../openbios/openbios-sparc64 %{buildroot}/%{_prefix}/share/qemu/openbios-sparc64
 
 
 
@@ -331,7 +363,7 @@ fi
 %doc %{qemudocdir}/COPYING 
 %doc %{qemudocdir}/COPYING.LIB 
 %doc %{qemudocdir}/LICENSE
-%{_prefix}/share/qemu/
+%dir %{_prefix}/share/qemu/
 %{_prefix}/share/qemu/keymaps/
 %{_mandir}/man1/qemu.1*
 %{_mandir}/man8/qemu-nbd.8*
@@ -364,6 +396,11 @@ fi
 %{_prefix}/share/qemu/bios.bin
 %{_prefix}/share/qemu/vgabios.bin
 %{_prefix}/share/qemu/vgabios-cirrus.bin
+%{_prefix}/share/qemu/pxe-e1000.bin
+%{_prefix}/share/qemu/pxe-virtio.bin
+%{_prefix}/share/qemu/pxe-pcnet.bin
+%{_prefix}/share/qemu/pxe-rtl8139.bin
+%{_prefix}/share/qemu/pxe-ne2k_pci.bin
 %ifarch %{ix86} x86_64
 %{_prefix}/share/qemu/extboot.bin
 %{_bindir}/qemu-kvm
@@ -393,6 +430,10 @@ fi
 %{_bindir}/qemu-system-ppc
 %{_bindir}/qemu-system-ppc64
 %{_bindir}/qemu-system-ppcemb
+%{_prefix}/share/qemu/openbios-ppc
+%{_prefix}/share/qemu/video.x
+%{_prefix}/share/qemu/bamboo.dtb
+%{_prefix}/share/qemu/ppc_rom.bin
 %files system-cris
 %defattr(-,root,root)
 %{_bindir}/qemu-system-cris
@@ -410,6 +451,18 @@ fi
 %{_mandir}/man1/qemu-img.1*
 
 %changelog
+* Wed Apr 01 2009 Glauber Costa <glommer@redhat.com> - 2:0.10-1
+- Include debuginfo for qemu-img
+- Do not require qemu-common for qemu-img
+- Explicitly own each of the firmware files
+- remove firmwares for ppc and sparc. They should be provided by an external package.
+  Not that the packages exists for sparc in the secondary arch repo as noarch, but they
+  don't automatically get into main repos. Unfortunately it's the best we can do right
+  now.
+- rollback a bit in time. Snapshot from avi's maint/2.6.30
+  - this requires the sasl patches to come back.
+  - with-patched-kernel comes back.
+
 * Wed Mar 25 2009 Mark McLoughlin <markmc@redhat.com> - 2:0.10-0.12.kvm20090323git
 - BuildRequires pciutils-devel for device assignment (#492076)
 
diff --git a/sources b/sources
index 286b835..004ec2d 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-67eac5a4ab26bfa159539d49bd30a89d  qemu-kvm-devel-85rc1.git-snapshot-20090323.tar.gz
+04c32fb43c722f180654f53e04ad17dd  qemu-kvm-0.10.tar.gz