diff --git a/.cvsignore b/.cvsignore index d7cbda4..a075ebc 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1 +1 @@ -kvm-84.git-snapshot-20090303.tar.gz +kvm-84.git-snapshot-20090310.tar.gz diff --git a/qemu-sasl-01-tls-handshake-fix.patch b/qemu-sasl-01-tls-handshake-fix.patch deleted file mode 100644 index ded8e4d..0000000 --- a/qemu-sasl-01-tls-handshake-fix.patch +++ /dev/null @@ -1,43 +0,0 @@ -This patch was previously posted here: - - http://lists.gnu.org/archive/html/qemu-devel/2009-02/msg00820.html - -In the case where the TLS handshake does *not* block on I/O, QEMU -sends the next 'start sub-auth' message twice. This seriously confuses -the VNC client :-) Fortunately the chances of the handshake not blocking -are close to zero for a TCP socket, which is why it has not been noticed -thus far. Even with both client & server on localhost, I can only hit the -bug 1 time in 20. - -NB, the diff context here is not too informative. If you look at the -full code you'll see that a few lines early we called vnc_start_tls() -which called vnc_continue_handshake() which called the method -start_auth_vencrypt_subauth(). Hence, fixing the bug, just involves -removing the 2nd bogus call to start_auth_vencrypt_subauth() as per -this patch. - - - vnc.c | 8 -------- - 1 file changed, 8 deletions(-) - - Signed-off-by: Daniel P. Berrange - -Index: kvm-84.git-snapshot-20090303/qemu/vnc.c -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc.c -+++ kvm-84.git-snapshot-20090303/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/qemu-sasl-02-vnc-monitor-info.patch b/qemu-sasl-02-vnc-monitor-info.patch deleted file mode 100644 index 3af5b99..0000000 --- a/qemu-sasl-02-vnc-monitor-info.patch +++ /dev/null @@ -1,192 +0,0 @@ -The current 'info vnc' monitor output just displays the VNC server address -as provided by the -vnc command line flag. This isn't particularly useful -since it doesn't tell you what VNC is actually listening on. eg, if you -use '-vnc :1' it is useful to know whether this translated to '0.0.0.0:5901' -or chose IPv6 ':::5901'. It is also useful to know the address of the -client that is currently connected. It is also useful to know the active -authentication (if any). - -This patch tweaks the monitor output to look like: - - (qemu) info vnc - Server: - address: 0.0.0.0:5902 - auth: vencrypt+x509 - Client: none - -And when 2 clients are connected - - (qemu) info vnc - Server: - address: 0.0.0.0:5902 - auth: vencrypt+x509 - Client: - address: 10.33.6.67:38621 - Client: - address: 10.33.6.63:38620 - -More data will be added to this later in the patch series... - -The 'addr_to_string' helper method in this patch is overly generic -for the needs of this patch alone. This is because it will be re-used -by the later SASL patches in this series, where the flexibility is -important. - - - vnc.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- - 1 file changed, 127 insertions(+), 10 deletions(-) - - Signed-off-by: Daniel P. Berrange - -Index: kvm-84.git-snapshot-20090303/qemu/vnc.c -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc.c -+++ kvm-84.git-snapshot-20090303/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/qemu-sasl-03-display-keymaps.patch b/qemu-sasl-03-display-keymaps.patch deleted file mode 100644 index 250deee..0000000 --- a/qemu-sasl-03-display-keymaps.patch +++ /dev/null @@ -1,352 +0,0 @@ -Each of the graphical frontends #include a .c file, for keymap code -resulting in duplicated definitions & duplicated compiled code. A -couple of small changes allowed this to be sanitized, so instead of -doing a #include "keymaps.c", duplicating all code, we can have a -shared keymaps.h file, and only compile code once. This allows the -next patch to move the VncState struct out into a header file without -causing clashing definitions. - - - Makefile | 9 +++++--- - b/keymaps.h | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - curses.c | 3 -- - curses_keys.h | 9 +++----- - keymaps.c | 45 ++++++++++++++++--------------------------- - sdl.c | 3 -- - sdl_keysym.h | 7 ++---- - vnc.c | 5 +--- - vnc_keysym.h | 7 ++---- - 9 files changed, 97 insertions(+), 51 deletions(-) - - Signed-off-by: Daniel P. Berrange - -Index: kvm-84.git-snapshot-20090303/qemu/Makefile -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/Makefile -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/curses.c -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/curses.c -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/curses_keys.h -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/curses_keys.h -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/keymaps.c -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/keymaps.c -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/keymaps.h -=================================================================== ---- /dev/null -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/sdl.c -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/sdl.c -+++ kvm-84.git-snapshot-20090303/qemu/sdl.c -@@ -107,7 +107,6 @@ static void sdl_resize(DisplayState *ds) - /* generic keyboard conversion */ - - #include "sdl_keysym.h" --#include "keymaps.c" - - static kbd_layout_t *kbd_layout = NULL; - -@@ -623,7 +622,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: kvm-84.git-snapshot-20090303/qemu/sdl_keysym.h -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/sdl_keysym.h -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/vnc.c -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc.c -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/vnc_keysym.h -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc_keysym.h -+++ kvm-84.git-snapshot-20090303/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/qemu-sasl-04-vnc-struct.patch b/qemu-sasl-04-vnc-struct.patch deleted file mode 100644 index 0863596..0000000 --- a/qemu-sasl-04-vnc-struct.patch +++ /dev/null @@ -1,315 +0,0 @@ -This patch moves the definitions of VncState and VncDisplay structs -out into a vnc.h header file. This is to allow the code for TLS -and SASL auth mechanisms to be moved out of the main vnc.c file. - - - vnc.c | 109 ------------------------------------------------ - vnc.h | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- - 2 files changed, 148 insertions(+), 110 deletions(-) - - Signed-off-by: Daniel P. Berrange - -Index: kvm-84.git-snapshot-20090303/qemu/vnc.c -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc.c -+++ kvm-84.git-snapshot-20090303/qemu/vnc.c -@@ -3,6 +3,7 @@ - * - * Copyright (C) 2006 Anthony Liguori - * Copyright (C) 2006 Fabrice Bellard -+ * Copyright (C) 2009 Red Hat, Inc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal -@@ -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 - - #define VNC_REFRESH_INTERVAL (1000 / 30) - --#include "vnc.h" - #include "vnc_keysym.h" - #include "d3des.h" - --#ifdef CONFIG_VNC_TLS --#include --#include --#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: kvm-84.git-snapshot-20090303/qemu/vnc.h -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc.h -+++ kvm-84.git-snapshot-20090303/qemu/vnc.h -@@ -1,5 +1,148 @@ --#ifndef __VNCTIGHT_H --#define __VNCTIGHT_H -+/* -+ * QEMU VNC display driver -+ * -+ * Copyright (C) 2006 Anthony Liguori -+ * Copyright (C) 2006 Fabrice Bellard -+ * Copyright (C) 2009 Red Hat, Inc -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a copy -+ * of this software and associated documentation files (the "Software"), to deal -+ * in the Software without restriction, including without limitation the rights -+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -+ * copies of the Software, and to permit persons to whom the Software is -+ * furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -+ * THE SOFTWARE. -+ */ -+ -+#ifndef __QEMU_VNC_H -+#define __QEMU_VNC_H -+ -+#include "qemu-common.h" -+#include "console.h" -+#include "audio/audio.h" -+#include -+ -+#ifdef CONFIG_VNC_TLS -+#include -+#include -+#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/qemu-sasl-05-vnc-tls-vencrypt.patch b/qemu-sasl-05-vnc-tls-vencrypt.patch deleted file mode 100644 index c9fadea..0000000 --- a/qemu-sasl-05-vnc-tls-vencrypt.patch +++ /dev/null @@ -1,1681 +0,0 @@ -This patch refactors the existing TLS code to make the main VNC code -more managable. The code moves to two new files - - - vnc-tls.c: generic helpers for TLS handshake & credential setup - - vnc-auth-vencrypt.c: the actual VNC TLS authentication mechanism. - -The reason for this split is that there are other TLS based auth -mechanisms which we may like to use in the future. These can all -share the same vnc-tls.c routines. In addition this will facilitate -anyone who may want to port the vnc-tls.c file to allow for choice -of GNUTLS & NSS for impl. - -The TLS state is moved out of the VncState struct, and into a separate -VncStateTLS struct, defined in vnc-tls.h. This is then referenced from -the main VncState. End size of the struct is the same, but it keeps -things a little more managable. - -The vnc.h file gains a bunch more function prototypes, for functions -in vnc.c that were previously static, but now need to be accessed -from the separate auth code files. - -The only TLS related code still in the main vl.c is the command line -argument handling / setup, and the low level I/O routines calling -gnutls_send/recv. - - - Makefile | 11 - b/vnc-auth-vencrypt.c | 167 ++++++++++++++ - b/vnc-auth-vencrypt.h | 33 ++ - b/vnc-tls.c | 414 +++++++++++++++++++++++++++++++++++ - b/vnc-tls.h | 70 ++++++ - vnc.c | 581 +++----------------------------------------------- - vnc.h | 76 ++++-- - 7 files changed, 780 insertions(+), 572 deletions(-) - - Signed-off-by: Daniel P. Berrange - -Index: kvm-84.git-snapshot-20090303/qemu/Makefile -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/Makefile -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/vnc-auth-vencrypt.c -=================================================================== ---- /dev/null -+++ kvm-84.git-snapshot-20090303/qemu/vnc-auth-vencrypt.c -@@ -0,0 +1,167 @@ -+/* -+ * QEMU VNC display driver: VeNCrypt authentication setup -+ * -+ * Copyright (C) 2006 Anthony Liguori -+ * Copyright (C) 2006 Fabrice Bellard -+ * Copyright (C) 2009 Red Hat, Inc -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a copy -+ * of this software and associated documentation files (the "Software"), to deal -+ * in the Software without restriction, including without limitation the rights -+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -+ * copies of the Software, and to permit persons to whom the Software is -+ * furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -+ * THE SOFTWARE. -+ */ -+ -+#include "vnc.h" -+ -+ -+static void start_auth_vencrypt_subauth(VncState *vs) -+{ -+ switch (vs->vd->subauth) { -+ case VNC_AUTH_VENCRYPT_TLSNONE: -+ case VNC_AUTH_VENCRYPT_X509NONE: -+ VNC_DEBUG("Accept TLS auth none\n"); -+ vnc_write_u32(vs, 0); /* Accept auth completion */ -+ start_client_init(vs); -+ break; -+ -+ case VNC_AUTH_VENCRYPT_TLSVNC: -+ case VNC_AUTH_VENCRYPT_X509VNC: -+ VNC_DEBUG("Start TLS auth VNC\n"); -+ start_auth_vnc(vs); -+ break; -+ -+ default: /* Should not be possible, but just in case */ -+ VNC_DEBUG("Reject auth %d\n", vs->vd->auth); -+ vnc_write_u8(vs, 1); -+ if (vs->minor >= 8) { -+ static const char err[] = "Unsupported authentication type"; -+ vnc_write_u32(vs, sizeof(err)); -+ vnc_write(vs, err, sizeof(err)); -+ } -+ vnc_client_error(vs); -+ } -+} -+ -+static void vnc_tls_handshake_io(void *opaque); -+ -+static int vnc_start_vencrypt_handshake(struct VncState *vs) { -+ int ret; -+ -+ if ((ret = gnutls_handshake(vs->tls.session)) < 0) { -+ if (!gnutls_error_is_fatal(ret)) { -+ VNC_DEBUG("Handshake interrupted (blocking)\n"); -+ if (!gnutls_record_get_direction(vs->tls.session)) -+ qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs); -+ else -+ qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs); -+ return 0; -+ } -+ VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret)); -+ vnc_client_error(vs); -+ return -1; -+ } -+ -+ if (vs->vd->tls.x509verify) { -+ if (vnc_tls_validate_certificate(vs) < 0) { -+ VNC_DEBUG("Client verification failed\n"); -+ vnc_client_error(vs); -+ return -1; -+ } else { -+ VNC_DEBUG("Client verification passed\n"); -+ } -+ } -+ -+ VNC_DEBUG("Handshake done, switching to TLS data mode\n"); -+ vs->tls.wiremode = VNC_WIREMODE_TLS; -+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs); -+ -+ start_auth_vencrypt_subauth(vs); -+ -+ return 0; -+} -+ -+static void vnc_tls_handshake_io(void *opaque) { -+ struct VncState *vs = (struct VncState *)opaque; -+ -+ VNC_DEBUG("Handshake IO continue\n"); -+ vnc_start_vencrypt_handshake(vs); -+} -+ -+ -+ -+#define NEED_X509_AUTH(vs) \ -+ ((vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509NONE || \ -+ (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509VNC || \ -+ (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN) -+ -+ -+static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len) -+{ -+ int auth = read_u32(data, 0); -+ -+ if (auth != vs->vd->subauth) { -+ VNC_DEBUG("Rejecting auth %d\n", auth); -+ vnc_write_u8(vs, 0); /* Reject auth */ -+ vnc_flush(vs); -+ vnc_client_error(vs); -+ } else { -+ VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth); -+ vnc_write_u8(vs, 1); /* Accept auth */ -+ vnc_flush(vs); -+ -+ if (vnc_tls_client_setup(vs, NEED_X509_AUTH(vs)) < 0) { -+ VNC_DEBUG("Failed to setup TLS\n"); -+ return 0; -+ } -+ -+ VNC_DEBUG("Start TLS VeNCrypt handshake process\n"); -+ if (vnc_start_vencrypt_handshake(vs) < 0) { -+ VNC_DEBUG("Failed to start TLS handshake\n"); -+ return 0; -+ } -+ } -+ return 0; -+} -+ -+static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len) -+{ -+ if (data[0] != 0 || -+ data[1] != 2) { -+ VNC_DEBUG("Unsupported VeNCrypt protocol %d.%d\n", (int)data[0], (int)data[1]); -+ vnc_write_u8(vs, 1); /* Reject version */ -+ vnc_flush(vs); -+ vnc_client_error(vs); -+ } else { -+ VNC_DEBUG("Sending allowed auth %d\n", vs->vd->subauth); -+ vnc_write_u8(vs, 0); /* Accept version */ -+ vnc_write_u8(vs, 1); /* Number of sub-auths */ -+ vnc_write_u32(vs, vs->vd->subauth); /* The supported auth */ -+ vnc_flush(vs); -+ vnc_read_when(vs, protocol_client_vencrypt_auth, 4); -+ } -+ return 0; -+} -+ -+ -+void start_auth_vencrypt(VncState *vs) -+{ -+ /* Send VeNCrypt version 0.2 */ -+ vnc_write_u8(vs, 0); -+ vnc_write_u8(vs, 2); -+ -+ vnc_read_when(vs, protocol_client_vencrypt_init, 2); -+} -+ -Index: kvm-84.git-snapshot-20090303/qemu/vnc-auth-vencrypt.h -=================================================================== ---- /dev/null -+++ kvm-84.git-snapshot-20090303/qemu/vnc-auth-vencrypt.h -@@ -0,0 +1,33 @@ -+/* -+ * QEMU VNC display driver -+ * -+ * Copyright (C) 2006 Anthony Liguori -+ * Copyright (C) 2006 Fabrice Bellard -+ * Copyright (C) 2009 Red Hat, Inc -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a copy -+ * of this software and associated documentation files (the "Software"), to deal -+ * in the Software without restriction, including without limitation the rights -+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -+ * copies of the Software, and to permit persons to whom the Software is -+ * furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -+ * THE SOFTWARE. -+ */ -+ -+ -+#ifndef __QEMU_VNC_AUTH_VENCRYPT_H__ -+#define __QEMU_VNC_AUTH_VENCRYPT_H__ -+ -+void start_auth_vencrypt(VncState *vs); -+ -+#endif /* __QEMU_VNC_AUTH_VENCRYPT_H__ */ -Index: kvm-84.git-snapshot-20090303/qemu/vnc-tls.c -=================================================================== ---- /dev/null -+++ kvm-84.git-snapshot-20090303/qemu/vnc-tls.c -@@ -0,0 +1,414 @@ -+/* -+ * QEMU VNC display driver: TLS helpers -+ * -+ * Copyright (C) 2006 Anthony Liguori -+ * Copyright (C) 2006 Fabrice Bellard -+ * Copyright (C) 2009 Red Hat, Inc -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a copy -+ * of this software and associated documentation files (the "Software"), to deal -+ * in the Software without restriction, including without limitation the rights -+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -+ * copies of the Software, and to permit persons to whom the Software is -+ * furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -+ * THE SOFTWARE. -+ */ -+ -+#include "vnc.h" -+#include "qemu_socket.h" -+ -+#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 -+/* Very verbose, so only enabled for _VNC_DEBUG >= 2 */ -+static void vnc_debug_gnutls_log(int level, const char* str) { -+ VNC_DEBUG("%d %s", level, str); -+} -+#endif /* defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 */ -+ -+ -+#define DH_BITS 1024 -+static gnutls_dh_params_t dh_params; -+ -+static int vnc_tls_initialize(void) -+{ -+ static int tlsinitialized = 0; -+ -+ if (tlsinitialized) -+ return 1; -+ -+ if (gnutls_global_init () < 0) -+ return 0; -+ -+ /* XXX ought to re-generate diffie-hellmen params periodically */ -+ if (gnutls_dh_params_init (&dh_params) < 0) -+ return 0; -+ if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0) -+ return 0; -+ -+#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 -+ gnutls_global_set_log_level(10); -+ gnutls_global_set_log_function(vnc_debug_gnutls_log); -+#endif -+ -+ tlsinitialized = 1; -+ -+ return 1; -+} -+ -+static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport, -+ const void *data, -+ size_t len) { -+ struct VncState *vs = (struct VncState *)transport; -+ int ret; -+ -+ retry: -+ ret = send(vs->csock, data, len, 0); -+ if (ret < 0) { -+ if (errno == EINTR) -+ goto retry; -+ return -1; -+ } -+ return ret; -+} -+ -+ -+static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport, -+ void *data, -+ size_t len) { -+ struct VncState *vs = (struct VncState *)transport; -+ int ret; -+ -+ retry: -+ ret = recv(vs->csock, data, len, 0); -+ if (ret < 0) { -+ if (errno == EINTR) -+ goto retry; -+ return -1; -+ } -+ return ret; -+} -+ -+ -+static gnutls_anon_server_credentials vnc_tls_initialize_anon_cred(void) -+{ -+ gnutls_anon_server_credentials anon_cred; -+ int ret; -+ -+ if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) { -+ VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret)); -+ return NULL; -+ } -+ -+ gnutls_anon_set_server_dh_params(anon_cred, dh_params); -+ -+ return anon_cred; -+} -+ -+ -+static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncDisplay *vd) -+{ -+ gnutls_certificate_credentials_t x509_cred; -+ int ret; -+ -+ if (!vd->tls.x509cacert) { -+ VNC_DEBUG("No CA x509 certificate specified\n"); -+ return NULL; -+ } -+ if (!vd->tls.x509cert) { -+ VNC_DEBUG("No server x509 certificate specified\n"); -+ return NULL; -+ } -+ if (!vd->tls.x509key) { -+ VNC_DEBUG("No server private key specified\n"); -+ return NULL; -+ } -+ -+ if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) { -+ VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret)); -+ return NULL; -+ } -+ if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred, -+ vd->tls.x509cacert, -+ GNUTLS_X509_FMT_PEM)) < 0) { -+ VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret)); -+ gnutls_certificate_free_credentials(x509_cred); -+ return NULL; -+ } -+ -+ if ((ret = gnutls_certificate_set_x509_key_file (x509_cred, -+ vd->tls.x509cert, -+ vd->tls.x509key, -+ GNUTLS_X509_FMT_PEM)) < 0) { -+ VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret)); -+ gnutls_certificate_free_credentials(x509_cred); -+ return NULL; -+ } -+ -+ if (vd->tls.x509cacrl) { -+ if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred, -+ vd->tls.x509cacrl, -+ GNUTLS_X509_FMT_PEM)) < 0) { -+ VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret)); -+ gnutls_certificate_free_credentials(x509_cred); -+ return NULL; -+ } -+ } -+ -+ gnutls_certificate_set_dh_params (x509_cred, dh_params); -+ -+ return x509_cred; -+} -+ -+ -+int vnc_tls_validate_certificate(struct VncState *vs) -+{ -+ int ret; -+ unsigned int status; -+ const gnutls_datum_t *certs; -+ unsigned int nCerts, i; -+ time_t now; -+ -+ VNC_DEBUG("Validating client certificate\n"); -+ if ((ret = gnutls_certificate_verify_peers2 (vs->tls.session, &status)) < 0) { -+ VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret)); -+ return -1; -+ } -+ -+ if ((now = time(NULL)) == ((time_t)-1)) { -+ return -1; -+ } -+ -+ if (status != 0) { -+ if (status & GNUTLS_CERT_INVALID) -+ VNC_DEBUG("The certificate is not trusted.\n"); -+ -+ if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) -+ VNC_DEBUG("The certificate hasn't got a known issuer.\n"); -+ -+ if (status & GNUTLS_CERT_REVOKED) -+ VNC_DEBUG("The certificate has been revoked.\n"); -+ -+ if (status & GNUTLS_CERT_INSECURE_ALGORITHM) -+ VNC_DEBUG("The certificate uses an insecure algorithm\n"); -+ -+ return -1; -+ } else { -+ VNC_DEBUG("Certificate is valid!\n"); -+ } -+ -+ /* Only support x509 for now */ -+ if (gnutls_certificate_type_get(vs->tls.session) != GNUTLS_CRT_X509) -+ return -1; -+ -+ if (!(certs = gnutls_certificate_get_peers(vs->tls.session, &nCerts))) -+ return -1; -+ -+ for (i = 0 ; i < nCerts ; i++) { -+ gnutls_x509_crt_t cert; -+ VNC_DEBUG ("Checking certificate chain %d\n", i); -+ if (gnutls_x509_crt_init (&cert) < 0) -+ return -1; -+ -+ if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) { -+ gnutls_x509_crt_deinit (cert); -+ return -1; -+ } -+ -+ if (gnutls_x509_crt_get_expiration_time (cert) < now) { -+ VNC_DEBUG("The certificate has expired\n"); -+ gnutls_x509_crt_deinit (cert); -+ return -1; -+ } -+ -+ if (gnutls_x509_crt_get_activation_time (cert) > now) { -+ VNC_DEBUG("The certificate is not yet activated\n"); -+ gnutls_x509_crt_deinit (cert); -+ return -1; -+ } -+ -+ if (gnutls_x509_crt_get_activation_time (cert) > now) { -+ VNC_DEBUG("The certificate is not yet activated\n"); -+ gnutls_x509_crt_deinit (cert); -+ return -1; -+ } -+ -+ gnutls_x509_crt_deinit (cert); -+ } -+ -+ return 0; -+} -+ -+ -+int vnc_tls_client_setup(struct VncState *vs, -+ int needX509Creds) { -+ static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 }; -+ static const int protocol_priority[]= { GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 }; -+ static const int kx_anon[] = {GNUTLS_KX_ANON_DH, 0}; -+ static const int kx_x509[] = {GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0}; -+ -+ VNC_DEBUG("Do TLS setup\n"); -+ if (vnc_tls_initialize() < 0) { -+ VNC_DEBUG("Failed to init TLS\n"); -+ vnc_client_error(vs); -+ return -1; -+ } -+ if (vs->tls.session == NULL) { -+ if (gnutls_init(&vs->tls.session, GNUTLS_SERVER) < 0) { -+ vnc_client_error(vs); -+ return -1; -+ } -+ -+ if (gnutls_set_default_priority(vs->tls.session) < 0) { -+ gnutls_deinit(vs->tls.session); -+ vs->tls.session = NULL; -+ vnc_client_error(vs); -+ return -1; -+ } -+ -+ if (gnutls_kx_set_priority(vs->tls.session, needX509Creds ? kx_x509 : kx_anon) < 0) { -+ gnutls_deinit(vs->tls.session); -+ vs->tls.session = NULL; -+ vnc_client_error(vs); -+ return -1; -+ } -+ -+ if (gnutls_certificate_type_set_priority(vs->tls.session, cert_type_priority) < 0) { -+ gnutls_deinit(vs->tls.session); -+ vs->tls.session = NULL; -+ vnc_client_error(vs); -+ return -1; -+ } -+ -+ if (gnutls_protocol_set_priority(vs->tls.session, protocol_priority) < 0) { -+ gnutls_deinit(vs->tls.session); -+ vs->tls.session = NULL; -+ vnc_client_error(vs); -+ return -1; -+ } -+ -+ if (needX509Creds) { -+ gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred(vs->vd); -+ if (!x509_cred) { -+ gnutls_deinit(vs->tls.session); -+ vs->tls.session = NULL; -+ vnc_client_error(vs); -+ return -1; -+ } -+ if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) { -+ gnutls_deinit(vs->tls.session); -+ vs->tls.session = NULL; -+ gnutls_certificate_free_credentials(x509_cred); -+ vnc_client_error(vs); -+ return -1; -+ } -+ if (vs->vd->tls.x509verify) { -+ VNC_DEBUG("Requesting a client certificate\n"); -+ gnutls_certificate_server_set_request (vs->tls.session, GNUTLS_CERT_REQUEST); -+ } -+ -+ } else { -+ gnutls_anon_server_credentials anon_cred = vnc_tls_initialize_anon_cred(); -+ if (!anon_cred) { -+ gnutls_deinit(vs->tls.session); -+ vs->tls.session = NULL; -+ vnc_client_error(vs); -+ return -1; -+ } -+ if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_ANON, anon_cred) < 0) { -+ gnutls_deinit(vs->tls.session); -+ vs->tls.session = NULL; -+ gnutls_anon_free_server_credentials(anon_cred); -+ vnc_client_error(vs); -+ return -1; -+ } -+ } -+ -+ gnutls_transport_set_ptr(vs->tls.session, (gnutls_transport_ptr_t)vs); -+ gnutls_transport_set_push_function(vs->tls.session, vnc_tls_push); -+ gnutls_transport_set_pull_function(vs->tls.session, vnc_tls_pull); -+ } -+ return 0; -+} -+ -+ -+void vnc_tls_client_cleanup(struct VncState *vs) -+{ -+ if (vs->tls.session) { -+ gnutls_deinit(vs->tls.session); -+ vs->tls.session = NULL; -+ } -+ vs->tls.wiremode = VNC_WIREMODE_CLEAR; -+} -+ -+ -+ -+static int vnc_set_x509_credential(VncDisplay *vd, -+ const char *certdir, -+ const char *filename, -+ char **cred, -+ int ignoreMissing) -+{ -+ struct stat sb; -+ -+ if (*cred) { -+ qemu_free(*cred); -+ *cred = NULL; -+ } -+ -+ *cred = qemu_malloc(strlen(certdir) + strlen(filename) + 2); -+ -+ strcpy(*cred, certdir); -+ strcat(*cred, "/"); -+ strcat(*cred, filename); -+ -+ VNC_DEBUG("Check %s\n", *cred); -+ if (stat(*cred, &sb) < 0) { -+ qemu_free(*cred); -+ *cred = NULL; -+ if (ignoreMissing && errno == ENOENT) -+ return 0; -+ return -1; -+ } -+ -+ return 0; -+} -+ -+ -+#define X509_CA_CERT_FILE "ca-cert.pem" -+#define X509_CA_CRL_FILE "ca-crl.pem" -+#define X509_SERVER_KEY_FILE "server-key.pem" -+#define X509_SERVER_CERT_FILE "server-cert.pem" -+ -+ -+int vnc_tls_set_x509_creds_dir(VncDisplay *vd, -+ const char *certdir) -+{ -+ if (vnc_set_x509_credential(vd, certdir, X509_CA_CERT_FILE, &vd->tls.x509cacert, 0) < 0) -+ goto cleanup; -+ if (vnc_set_x509_credential(vd, certdir, X509_CA_CRL_FILE, &vd->tls.x509cacrl, 1) < 0) -+ goto cleanup; -+ if (vnc_set_x509_credential(vd, certdir, X509_SERVER_CERT_FILE, &vd->tls.x509cert, 0) < 0) -+ goto cleanup; -+ if (vnc_set_x509_credential(vd, certdir, X509_SERVER_KEY_FILE, &vd->tls.x509key, 0) < 0) -+ goto cleanup; -+ -+ return 0; -+ -+ cleanup: -+ qemu_free(vd->tls.x509cacert); -+ qemu_free(vd->tls.x509cacrl); -+ qemu_free(vd->tls.x509cert); -+ qemu_free(vd->tls.x509key); -+ vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL; -+ return -1; -+} -+ -Index: kvm-84.git-snapshot-20090303/qemu/vnc-tls.h -=================================================================== ---- /dev/null -+++ kvm-84.git-snapshot-20090303/qemu/vnc-tls.h -@@ -0,0 +1,70 @@ -+/* -+ * QEMU VNC display driver. TLS helpers -+ * -+ * Copyright (C) 2006 Anthony Liguori -+ * Copyright (C) 2006 Fabrice Bellard -+ * Copyright (C) 2009 Red Hat, Inc -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a copy -+ * of this software and associated documentation files (the "Software"), to deal -+ * in the Software without restriction, including without limitation the rights -+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -+ * copies of the Software, and to permit persons to whom the Software is -+ * furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -+ * THE SOFTWARE. -+ */ -+ -+ -+#ifndef __QEMU_VNC_TLS_H__ -+#define __QEMU_VNC_TLS_H__ -+ -+#include -+#include -+ -+enum { -+ VNC_WIREMODE_CLEAR, -+ VNC_WIREMODE_TLS, -+}; -+ -+typedef struct VncDisplayTLS VncDisplayTLS; -+typedef struct VncStateTLS VncStateTLS; -+ -+/* Server state */ -+struct VncDisplayTLS { -+ int x509verify; /* Non-zero if server requests & validates client cert */ -+ -+ /* Paths to x509 certs/keys */ -+ char *x509cacert; -+ char *x509cacrl; -+ char *x509cert; -+ char *x509key; -+}; -+ -+/* Per client state */ -+struct VncStateTLS { -+ /* Whether data is being TLS encrypted yet */ -+ int wiremode; -+ gnutls_session_t session; -+}; -+ -+int vnc_tls_client_setup(VncState *vs, int x509Creds); -+void vnc_tls_client_cleanup(VncState *vs); -+ -+int vnc_tls_validate_certificate(VncState *vs); -+ -+int vnc_tls_set_x509_creds_dir(VncDisplay *vd, -+ const char *path); -+ -+ -+#endif /* __QEMU_VNC_TLS_H__ */ -+ -Index: kvm-84.git-snapshot-20090303/qemu/vnc.c -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc.c -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/vnc.h -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc.h -+++ kvm-84.git-snapshot-20090303/qemu/vnc.h -@@ -32,13 +32,16 @@ - #include "audio/audio.h" - #include - --#ifdef CONFIG_VNC_TLS --#include --#include --#endif /* CONFIG_VNC_TLS */ -- - #include "keymaps.h" - -+// #define _VNC_DEBUG 1 -+ -+#ifdef _VNC_DEBUG -+#define VNC_DEBUG(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) -+#else -+#define VNC_DEBUG(fmt, ...) do { } while (0) -+#endif -+ - /***************************************************************************** - * - * Core data structures -@@ -72,6 +75,11 @@ typedef void VncSendHextileTile(VncState - - typedef struct VncDisplay VncDisplay; - -+#ifdef CONFIG_VNC_TLS -+#include "vnc-tls.h" -+#include "vnc-auth-vencrypt.h" -+#endif -+ - struct VncDisplay - { - int lsock; -@@ -83,13 +91,8 @@ struct VncDisplay - char *password; - int auth; - #ifdef CONFIG_VNC_TLS -- int subauth; -- int x509verify; -- -- char *x509cacert; -- char *x509cacrl; -- char *x509cert; -- char *x509key; -+ int subauth; /* Used by VeNCrypt */ -+ VncDisplayTLS tls; - #endif - }; - -@@ -117,8 +120,7 @@ struct VncState - char challenge[VNC_AUTH_CHALLENGE_SIZE]; - - #ifdef CONFIG_VNC_TLS -- int wiremode; -- gnutls_session_t tls_session; -+ VncStateTLS tls; - #endif - - Buffer output; -@@ -162,12 +164,6 @@ enum { - VNC_AUTH_VENCRYPT = 19 - }; - --#ifdef CONFIG_VNC_TLS --enum { -- VNC_WIREMODE_CLEAR, -- VNC_WIREMODE_TLS, --}; -- - enum { - VNC_AUTH_VENCRYPT_PLAIN = 256, - VNC_AUTH_VENCRYPT_TLSNONE = 257, -@@ -178,12 +174,6 @@ enum { - VNC_AUTH_VENCRYPT_X509PLAIN = 262, - }; - --#define X509_CA_CERT_FILE "ca-cert.pem" --#define X509_CA_CRL_FILE "ca-crl.pem" --#define X509_SERVER_KEY_FILE "server-key.pem" --#define X509_SERVER_CERT_FILE "server-cert.pem" -- --#endif /* CONFIG_VNC_TLS */ - - /***************************************************************************** - * -@@ -254,4 +244,38 @@ enum { - #define VNC_FEATURE_ZLIB_MASK (1 << VNC_FEATURE_ZLIB) - #define VNC_FEATURE_COPYRECT_MASK (1 << VNC_FEATURE_COPYRECT) - -+ -+/***************************************************************************** -+ * -+ * Internal APIs -+ * -+ *****************************************************************************/ -+ -+/* Event loop functions */ -+void vnc_client_read(void *opaque); -+void vnc_client_write(void *opaque); -+ -+ -+/* Protocol I/O functions */ -+void vnc_write(VncState *vs, const void *data, size_t len); -+void vnc_write_u32(VncState *vs, uint32_t value); -+void vnc_write_s32(VncState *vs, int32_t value); -+void vnc_write_u16(VncState *vs, uint16_t value); -+void vnc_write_u8(VncState *vs, uint8_t value); -+void vnc_flush(VncState *vs); -+void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting); -+ -+ -+/* Buffer I/O functions */ -+uint8_t read_u8(uint8_t *data, size_t offset); -+uint16_t read_u16(uint8_t *data, size_t offset); -+int32_t read_s32(uint8_t *data, size_t offset); -+uint32_t read_u32(uint8_t *data, size_t offset); -+ -+/* Protocol stage functions */ -+void vnc_client_error(VncState *vs); -+ -+void start_client_init(VncState *vs); -+void start_auth_vnc(VncState *vs); -+ - #endif /* __QEMU_VNC_H */ diff --git a/qemu-sasl-06-vnc-sasl.patch b/qemu-sasl-06-vnc-sasl.patch deleted file mode 100644 index 388d6c6..0000000 --- a/qemu-sasl-06-vnc-sasl.patch +++ /dev/null @@ -1,1603 +0,0 @@ -This patch adds the new SASL authentication protocol to the VNC server. - -It is enabled by setting the 'sasl' flag when launching VNC. SASL can -optionally provide encryption via its SSF layer, if a suitable mechanism -is configured (eg, GSSAPI/Kerberos, or Digest-MD5). If an SSF layer is -not available, then it should be combined with the x509 VNC authentication -protocol which provides encryption. - -eg, if using GSSAPI - - qemu -vnc localhost:1,sasl - -eg if using TLS/x509 for encryption - - qemu -vnc localhost:1,sasl,tls,x509 - - -By default the Cyrus SASL library will look for its configuration in -the file /etc/sasl2/qemu.conf. For non-root users, this can be overridden -by setting the SASL_CONF_PATH environment variable, eg to make it look in -$HOME/.sasl2. NB unprivileged users may not have access to the full range -of SASL mechanisms, since some of them require some administrative privileges -to configure. The patch includes an example SASL configuration file which -illustrates config for GSSAPI and Digest-MD5, though it should be noted that -the latter is not really considered secure any more. - -Most of the SASL authentication code is located in a separate source file, -vnc-auth-sasl.c. The main vnc.c file only contains minimal integration -glue, specifically parsing of command line flags / setup, and calls to -start the SASL auth process, to do encoding/decoding for data. - -There are several possible stacks for reading & writing of data, depending -on the combo of VNC authentication methods in use - - - Clear. read/write straight to socket - - TLS. read/write via GNUTLS helpers - - SASL. encode/decode via SASL SSF layer, then read/write to socket - - SASL+TLS. encode/decode via SASL SSF layer, then read/write via GNUTLS - -Hence, the vnc_client_read & vnc_client_write methods have been refactored -a little. - - vnc_client_read: main entry point for reading, calls either - - - vnc_client_read_plain reading, with no intermediate decoding - - vnc_client_read_sasl reading, with SASL SSF decoding - - These two methods, then call vnc_client_read_buf(). This decides - whether to write to the socket directly or write via GNUTLS. - -The situation is the same for writing data. More extensive comments -have been added in the code / patch. The vnc_client_read_sasl and -vnc_client_write_sasl method implementations live in the separate -vnc-auth-sasl.c file. - -The state required for the SASL auth mechanism is kept in a separate -VncStateSASL struct, defined in vnc-auth-sasl.h and included in the -main VncState. - -The configure script probes for SASL and automatically enables it -if found, unless --disable-vnc-sasl was given to override it. - - - Makefile | 7 - Makefile.target | 5 - b/qemu.sasl | 34 ++ - b/vnc-auth-sasl.c | 626 ++++++++++++++++++++++++++++++++++++++++++++++++++++ - b/vnc-auth-sasl.h | 67 +++++ - configure | 34 ++ - qemu-doc.texi | 97 ++++++++ - vnc-auth-vencrypt.c | 12 - vnc.c | 249 ++++++++++++++++++-- - vnc.h | 31 ++ - 10 files changed, 1129 insertions(+), 33 deletions(-) - - Signed-off-by: Daniel P. Berrange - -Index: kvm-84.git-snapshot-20090303/qemu/Makefile -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/Makefile -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/Makefile.target -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/Makefile.target -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/configure -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/configure -+++ kvm-84.git-snapshot-20090303/qemu/configure -@@ -164,6 +164,7 @@ fmod_lib="" - fmod_inc="" - oss_lib="" - vnc_tls="yes" -+vnc_sasl="yes" - bsd="no" - linux="no" - solaris="no" -@@ -403,6 +404,8 @@ for opt do - ;; - --disable-vnc-tls) vnc_tls="no" - ;; -+ --disable-vnc-sasl) vnc_sasl="no" -+ ;; - --disable-slirp) slirp="no" - ;; - --disable-vde) vde="no" -@@ -562,6 +565,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" -@@ -874,6 +878,25 @@ EOF - fi - - ########################################## -+# VNC SASL detection -+if test "$vnc_sasl" = "yes" ; then -+cat > $TMPC < -+#include -+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 -@@ -1208,6 +1231,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 -@@ -1451,6 +1479,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: kvm-84.git-snapshot-20090303/qemu/qemu-doc.texi -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/qemu-doc.texi -+++ kvm-84.git-snapshot-20090303/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 -@@ -2058,7 +2073,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 -@@ -2141,6 +2159,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 - -@@ -2252,6 +2305,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: kvm-84.git-snapshot-20090303/qemu/qemu.sasl -=================================================================== ---- /dev/null -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/vnc-auth-sasl.c -=================================================================== ---- /dev/null -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/vnc-auth-sasl.h -=================================================================== ---- /dev/null -+++ kvm-84.git-snapshot-20090303/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 -+ -+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: kvm-84.git-snapshot-20090303/qemu/vnc-auth-vencrypt.c -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc-auth-vencrypt.c -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/vnc.c -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc.c -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/vnc.h -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc.h -+++ kvm-84.git-snapshot-20090303/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/qemu-sasl-07-vnc-monitor-authinfo.patch b/qemu-sasl-07-vnc-monitor-authinfo.patch deleted file mode 100644 index 2c80faa..0000000 --- a/qemu-sasl-07-vnc-monitor-authinfo.patch +++ /dev/null @@ -1,124 +0,0 @@ -This patch extends the 'info vnc' monitor output to include information -about the VNC client authentication credentials. - -For clients authenticated using SASL, this will output the username. - -For clients authenticated using x509 certificates, this will output -the x509 distinguished name. - -Auth can be stacked, so both username & x509 dname may be shown. - - Server: - address: 0.0.0.0:5902 - auth: vencrypt+x509+sasl - Client: - address: 10.33.6.67:38621 - x509 dname: C=GB,O=ACME,L=London,ST=London,CN=localhost - username: admin - Client: - address: 10.33.6.63:38620 - x509 dname: C=GB,O=ACME,L=London,ST=London,CN=localhost - username: admin - - - - vnc-tls.c | 17 +++++++++++++++++ - vnc-tls.h | 3 +++ - vnc.c | 19 +++++++++++++++++-- - 3 files changed, 37 insertions(+), 2 deletions(-) - - Signed-off-by: Daniel P. Berrange - -Index: kvm-84.git-snapshot-20090303/qemu/vnc-tls.c -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc-tls.c -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/vnc-tls.h -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc-tls.h -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/vnc.c -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc.c -+++ kvm-84.git-snapshot-20090303/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/qemu-sasl-08-vnc-acl-mgmt.patch b/qemu-sasl-08-vnc-acl-mgmt.patch deleted file mode 100644 index 271925f..0000000 --- a/qemu-sasl-08-vnc-acl-mgmt.patch +++ /dev/null @@ -1,781 +0,0 @@ -This patch introduces a generic internal API for access control lists -to be used by network servers in QEMU. It adds support for checking -these ACL in the VNC server, in two places. The first ACL is for the -SASL authentication mechanism, checking the SASL username. This ACL -is called 'vnc.username'. The second is for the TLS authentication -mechanism, when x509 client certificates are turned on, checking against -the Distinguished Name of the client. This ACL is called 'vnc.x509dname' - -The internal API provides for an ACL with the following characteristics - - - A unique name, eg vnc.username, and vnc.x509dname. - - A default policy, allow or deny - - An ordered series of match rules, with allow or deny policy - -If none of the match rules apply, then the default policy is -used. - -There is a monitor API to manipulate the ACLs, which I'll describe via -examples - - (qemu) acl show vnc.username - policy: allow - (qemu) acl policy vnc.username denya - acl: policy set to 'deny' - (qemu) acl allow vnc.username fred - acl: added rule at position 1 - (qemu) acl allow vnc.username bob - acl: added rule at position 2 - (qemu) acl allow vnc.username joe 1 - acl: added rule at position 1 - (qemu) acl show vnc.username - policy: deny - 0: allow fred - 1: allow joe - 2: allow bob - - - (qemu) acl show vnc.x509dname - policy: allow - (qemu) acl policy vnc.x509dname deny - acl: policy set to 'deny' - (qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=* - acl: added rule at position 1 - (qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob - acl: added rule at position 2 - (qemu) acl show vnc.x509dname - policy: deny - 0: allow C=GB,O=ACME,L=London,CN=* - 1: allow C=GB,O=ACME,L=Boston,CN=bob - -By default the VNC server will not use any ACLs, allowing access to -the server if the user successfully authenticates. To enable use of -ACLs to restrict user access, the ',acl' flag should be given when -starting QEMU. The initial ACL activated will be a 'deny all' policy -and should be customized using monitor commands. - -eg enable SASL auth and ACLs - - qemu .... -vnc localhost:1,sasl,acl - -The next patch will provide a way to load a pre-defined ACL when -starting up - - - Makefile | 6 + - b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - b/acl.h | 74 ++++++++++++++++++++++ - configure | 18 +++++ - monitor.c | 95 ++++++++++++++++++++++++++++ - qemu-doc.texi | 49 ++++++++++++++ - vnc-auth-sasl.c | 16 +++- - vnc-auth-sasl.h | 7 ++ - vnc-tls.c | 19 +++++ - vnc-tls.h | 3 - vnc.c | 21 ++++++ - vnc.h | 3 - 12 files changed, 491 insertions(+), 5 deletions(-) - - Signed-off-by: Daniel P. Berrange - -Index: kvm-84.git-snapshot-20090303/qemu/Makefile -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/Makefile -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/acl.c -=================================================================== ---- /dev/null -+++ kvm-84.git-snapshot-20090303/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 -+#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: kvm-84.git-snapshot-20090303/qemu/acl.h -=================================================================== ---- /dev/null -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/configure -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/configure -+++ kvm-84.git-snapshot-20090303/qemu/configure -@@ -897,6 +897,21 @@ EOF - fi - - ########################################## -+# fnmatch() probe, used for ACL routines -+fnmatch="no" -+cat > $TMPC << EOF -+#include -+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 -@@ -1485,6 +1500,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: kvm-84.git-snapshot-20090303/qemu/monitor.c -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/monitor.c -+++ kvm-84.git-snapshot-20090303/qemu/monitor.c -@@ -39,6 +39,7 @@ - #include "qemu-timer.h" - #include "migration.h" - #include "kvm.h" -+#include "acl.h" - - #include "qemu-kvm.h" - -@@ -1452,6 +1453,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, -@@ -1557,6 +1637,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, " [] []\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" -@@ -2927,3 +3013,12 @@ void monitor_readline(const char *prompt - monitor_hd[i]->focus = old_focus[i]; - } - } -+ -+ -+/* -+ * Local variables: -+ * c-indent-level: 4 -+ * c-basic-offset: 4 -+ * tab-width: 8 -+ * End: -+ */ -Index: kvm-84.git-snapshot-20090303/qemu/qemu-doc.texi -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/qemu-doc.texi -+++ kvm-84.git-snapshot-20090303/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 -@@ -1389,6 +1402,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 -+list all the match rules in the access control list, and the default -+policy -+@item acl policy @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 [] -+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 [] -+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 -+remove the specified match rule from the access control list. -+@item acl reset -+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: kvm-84.git-snapshot-20090303/qemu/vnc-auth-sasl.c -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc-auth-sasl.c -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/vnc-auth-sasl.h -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc-auth-sasl.h -+++ kvm-84.git-snapshot-20090303/qemu/vnc-auth-sasl.h -@@ -30,6 +30,9 @@ - #include - - 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: kvm-84.git-snapshot-20090303/qemu/vnc-tls.c -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc-tls.c -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/vnc-tls.h -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc-tls.h -+++ kvm-84.git-snapshot-20090303/qemu/vnc-tls.h -@@ -31,6 +31,8 @@ - #include - #include - -+#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: kvm-84.git-snapshot-20090303/qemu/vnc.c -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc.c -+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/vnc.h -=================================================================== ---- kvm-84.git-snapshot-20090303.orig/qemu/vnc.h -+++ kvm-84.git-snapshot-20090303/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/qemu.spec b/qemu.spec index df8906c..9d0203f 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.5.kvm20090303git%{?dist} +Release: 0.6.kvm20090310git%{?dist} # I have mistakenly thought the revision name would be 1.0. # So 0.10 series get Epoch = 1 Epoch: 2 @@ -10,25 +10,12 @@ Group: Development/Tools URL: http://www.qemu.org/ #Source0: http://www.qemu.org/%{name}-%{version}.tar.gz # FIXME: Say how to get the sources -Source0: kvm-84.git-snapshot-20090303.tar.gz +Source0: kvm-84.git-snapshot-20090310.tar.gz Source1: qemu.init Source2: kvm.modules -# VNC SASL authentication support -# Not upstream yet, but approved for commit immediately -# after this release -Patch1: qemu-sasl-01-tls-handshake-fix.patch -Patch2: qemu-sasl-02-vnc-monitor-info.patch -Patch3: qemu-sasl-03-display-keymaps.patch -Patch4: qemu-sasl-04-vnc-struct.patch -Patch5: qemu-sasl-05-vnc-tls-vencrypt.patch -Patch6: qemu-sasl-06-vnc-sasl.patch -Patch7: qemu-sasl-07-vnc-monitor-authinfo.patch -Patch8: qemu-sasl-08-vnc-acl-mgmt.patch -Patch9: kvm-upstream-ppc.patch -Patch10: kvm-fix-strayR.patch -# NB, delibrately not including patch 09 which is not -# intended for commit +Patch1: kvm-upstream-ppc.patch +Patch2: kvm-fix-strayR.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: SDL-devel zlib-devel which texi2html gnutls-devel cyrus-sasl-devel @@ -190,25 +177,9 @@ such as kvmtrace and kvm_stat. %endif %prep -%setup -q -n kvm-84.git-snapshot-20090303 -# 01-tls-handshake-fix +%setup -q -n kvm-84.git-snapshot-20090310 %patch1 -p1 -# 02-vnc-monitor-info %patch2 -p1 -# 03-display-keymaps -%patch3 -p1 -# 04-vnc-struct -%patch4 -p1 -# 05-vnc-tls-vencrypt -%patch5 -p1 -# 06-vnc-sasl -%patch6 -p1 -# 07-vnc-monitor-authinfo -%patch7 -p1 -# 08-vnc-acl-mgmt -%patch8 -p1 -%patch9 -p1 -%patch10 -p1 %build # systems like rhel build system does not have a recent enough linker so @@ -258,7 +229,7 @@ cd qemu sparc32plus-linux-user" \ --prefix=%{_prefix} \ --interp-prefix=%{_prefix}/qemu-%%M \ - --kerneldir=$(pwd)/../kernel --prefix=%{_prefix} \ + --kerneldir=$(pwd)/../kernel --prefix=%{_prefix} \ --disable-kvm \ --extra-ldflags=$extraldflags @@ -438,6 +409,10 @@ fi %{_mandir}/man1/qemu-img.1* %changelog +* Tue Mar 10 2009 Glauber Costa - 2:0.10-0.6.kvm20090310git +- updated to kvm20090310git +- removed sasl patches (already in this release) + * Tue Mar 10 2009 Glauber Costa - 2:0.10-0.5.kvm20090303git - kvm.modules were being wrongly mentioned at %%install. - update description for the x86 system package to include kvm support diff --git a/sources b/sources index 0877184..f4afd25 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -b81c0982015b21e01fe93a1d9405def8 kvm-84.git-snapshot-20090303.tar.gz +a43cc777acc08855e0364d249bff3da6 kvm-84.git-snapshot-20090310.tar.gz