qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] PATCH 0/8: Authentication support for the VNC server
@ 2007-07-31 19:23 Daniel P. Berrange
  2007-07-31 19:25 ` [Qemu-devel] PATCH 1/8: Refactor VNC server setup API Daniel P. Berrange
                   ` (8 more replies)
  0 siblings, 9 replies; 19+ messages in thread
From: Daniel P. Berrange @ 2007-07-31 19:23 UTC (permalink / raw)
  To: qemu-devel

The current VNC server implementation does not have support for the
authentication of incoming client connections. The following series
of patches provide support for a number of alternatives, all compliant
with the VNC protocol spec. The simplest mechanism (and the weakest)
is the traditional VNC password scheme based on weak d3des hashing of
an 8 byte key. The more serious mechanism uses TLS for data encryption
of the entire session, and x509 certificates for both client and server
authentication.

The patches are an iteration on the previous work I posted a couple of
months ago[1]. They are now functionally complete, better tested,
split up into a patch series for easier review, and their use fully
documented. Since TLS can be quite perplexing, I also included some
documentation on how to setup a CA, and issue client & server certs
in a manner suitable for use with the VNC server.

For the basic VNC password auth, this patch should be compatible with
any standard VNC client such as RealVNC. The TLS based auth schemes
require a client that implements the VeNCrypt extension[2]. The client
from the VeNCrypt[3] project of course is one example. The GTK-VNC[4]
widget which is used by Virt Manager[5] and Vinagre [6] also support
it, and are my primary testing platform.

The 8 individual patches will follow shortly in replies to this mail.

Regards,
Dan.

[1] http://www.mail-archive.com/qemu-devel@nongnu.org/msg08616.html
[2] http://www.mail-archive.com/qemu-devel@nongnu.org/msg08681.html
[3] http://sourceforge.net/projects/vencrypt/
[4] http://gtk-vnc.sourceforge.net/
[5] http://virt-manager.org/
[6] http://www.gnome.org/~jwendell/vinagre/
-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Qemu-devel] PATCH 1/8: Refactor VNC server setup API
  2007-07-31 19:23 [Qemu-devel] PATCH 0/8: Authentication support for the VNC server Daniel P. Berrange
@ 2007-07-31 19:25 ` Daniel P. Berrange
  2007-07-31 19:25 ` [Qemu-devel] PATCH 2/8: Extend monitor 'change' command for VNC Daniel P. Berrange
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 19+ messages in thread
From: Daniel P. Berrange @ 2007-07-31 19:25 UTC (permalink / raw)
  To: qemu-devel

This patch splits the vnc_display_init function into two parts,
the resulting vnc_display_init function merely initializes a
little state. The new vnc_display_open function is responsible
for starting the server. This refactoring is in preparation for
the next patch.

diff -r cdd882fa7018 vl.c
--- a/vl.c	Wed Jul 25 11:13:43 2007 -0400
+++ b/vl.c	Tue Jul 31 10:37:39 2007 -0400
@@ -7932,7 +7932,9 @@ int main(int argc, char **argv)
         /* nearly nothing to do */
         dumb_display_init(ds);
     } else if (vnc_display != NULL) {
-        vnc_display_init(ds, vnc_display);
+        vnc_display_init(ds);
+        if (vnc_display_open(ds, vnc_display) < 0)
+            exit(1);
     } else {
 #if defined(CONFIG_SDL)
         sdl_display_init(ds, full_screen, no_frame);
diff -r cdd882fa7018 vl.h
--- a/vl.h	Wed Jul 25 11:13:43 2007 -0400
+++ b/vl.h	Tue Jul 31 10:34:10 2007 -0400
@@ -966,7 +966,9 @@ void cocoa_display_init(DisplayState *ds
 void cocoa_display_init(DisplayState *ds, int full_screen);
 
 /* vnc.c */
-void vnc_display_init(DisplayState *ds, const char *display);
+void vnc_display_init(DisplayState *ds);
+void vnc_display_close(DisplayState *ds);
+int vnc_display_open(DisplayState *ds, const char *display);
 void do_info_vnc(void);
 
 /* x_keymap.c */
diff -r cdd882fa7018 vnc.c
--- a/vnc.c	Wed Jul 25 11:13:43 2007 -0400
+++ b/vnc.c	Tue Jul 31 10:45:11 2007 -0400
@@ -73,7 +73,7 @@ struct VncState
     int last_x;
     int last_y;
 
-    const char *display;
+    char *display;
 
     Buffer output;
     Buffer input;
@@ -1169,7 +1169,58 @@ static void vnc_listen_read(void *opaque
 
 extern int parse_host_port(struct sockaddr_in *saddr, const char *str);
 
-void vnc_display_init(DisplayState *ds, const char *arg)
+void vnc_display_init(DisplayState *ds)
+{
+    VncState *vs;
+
+    vs = qemu_mallocz(sizeof(VncState));
+    if (!vs)
+	exit(1);
+
+    ds->opaque = vs;
+    vnc_state = vs;
+    vs->display = NULL;
+
+    vs->lsock = -1;
+    vs->csock = -1;
+    vs->depth = 4;
+    vs->last_x = -1;
+    vs->last_y = -1;
+
+    vs->ds = ds;
+
+    if (!keyboard_layout)
+	keyboard_layout = "en-us";
+
+    vs->kbd_layout = init_keyboard_layout(keyboard_layout);
+    if (!vs->kbd_layout)
+	exit(1);
+
+    vs->ds->data = NULL;
+    vs->ds->dpy_update = vnc_dpy_update;
+    vs->ds->dpy_resize = vnc_dpy_resize;
+    vs->ds->dpy_refresh = vnc_dpy_refresh;
+
+    memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));
+
+    vnc_dpy_resize(vs->ds, 640, 400);
+}
+
+void vnc_display_close(DisplayState *ds)
+{
+    VncState *vs = (VncState *)ds->opaque;
+
+    if (vs->display) {
+	qemu_free(vs->display);
+	vs->display = NULL;
+    }
+    if (vs->lsock != -1) {
+	close(vs->lsock);
+	vs->lsock = -1;
+    }
+}
+
+int vnc_display_open(DisplayState *ds, const char *arg)
 {
     struct sockaddr *addr;
     struct sockaddr_in iaddr;
@@ -1179,40 +1230,14 @@ void vnc_display_init(DisplayState *ds, 
     int reuse_addr, ret;
     socklen_t addrlen;
     const char *p;
-    VncState *vs;
-
-    vs = qemu_mallocz(sizeof(VncState));
-    if (!vs)
-	exit(1);
-
-    ds->opaque = vs;
-    vnc_state = vs;
-    vs->display = arg;
-
-    vs->lsock = -1;
-    vs->csock = -1;
-    vs->depth = 4;
-    vs->last_x = -1;
-    vs->last_y = -1;
-
-    vs->ds = ds;
-
-    if (!keyboard_layout)
-	keyboard_layout = "en-us";
-
-    vs->kbd_layout = init_keyboard_layout(keyboard_layout);
-    if (!vs->kbd_layout)
-	exit(1);
-
-    vs->ds->data = NULL;
-    vs->ds->dpy_update = vnc_dpy_update;
-    vs->ds->dpy_resize = vnc_dpy_resize;
-    vs->ds->dpy_refresh = vnc_dpy_refresh;
-
-    memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));
-
-    vnc_dpy_resize(vs->ds, 640, 400);
-
+    VncState *vs = (VncState *)ds->opaque;
+
+    vnc_display_close(ds);
+    if (strcmp(arg, "none") == 0)
+	return 0;
+
+    if (!(vs->display = strdup(arg)))
+	return -1;
 #ifndef _WIN32
     if (strstart(arg, "unix:", &p)) {
 	addr = (struct sockaddr *)&uaddr;
@@ -1221,7 +1246,9 @@ void vnc_display_init(DisplayState *ds, 
 	vs->lsock = socket(PF_UNIX, SOCK_STREAM, 0);
 	if (vs->lsock == -1) {
 	    fprintf(stderr, "Could not create socket\n");
-	    exit(1);
+	    free(vs->display);
+	    vs->display = NULL;
+	    return -1;
 	}
 
 	uaddr.sun_family = AF_UNIX;
@@ -1235,40 +1262,53 @@ void vnc_display_init(DisplayState *ds, 
 	addr = (struct sockaddr *)&iaddr;
 	addrlen = sizeof(iaddr);
 
+	if (parse_host_port(&iaddr, arg) < 0) {
+	    fprintf(stderr, "Could not parse VNC address\n");
+	    free(vs->display);
+	    vs->display = NULL;
+	    return -1;
+	}
+
+	iaddr.sin_port = htons(ntohs(iaddr.sin_port) + 5900);
+
 	vs->lsock = socket(PF_INET, SOCK_STREAM, 0);
 	if (vs->lsock == -1) {
 	    fprintf(stderr, "Could not create socket\n");
-	    exit(1);
+	    free(vs->display);
+	    vs->display = NULL;
+	    return -1;
 	}
-
-	if (parse_host_port(&iaddr, arg) < 0) {
-	    fprintf(stderr, "Could not parse VNC address\n");
-	    exit(1);
-	}
-	    
-	iaddr.sin_port = htons(ntohs(iaddr.sin_port) + 5900);
 
 	reuse_addr = 1;
 	ret = setsockopt(vs->lsock, SOL_SOCKET, SO_REUSEADDR,
 			 (const char *)&reuse_addr, sizeof(reuse_addr));
 	if (ret == -1) {
 	    fprintf(stderr, "setsockopt() failed\n");
-	    exit(1);
+	    close(vs->lsock);
+	    vs->lsock = -1;
+	    free(vs->display);
+	    vs->display = NULL;
+	    return -1;
 	}
     }
 
     if (bind(vs->lsock, addr, addrlen) == -1) {
 	fprintf(stderr, "bind() failed\n");
-	exit(1);
+	close(vs->lsock);
+	vs->lsock = -1;
+	free(vs->display);
+	vs->display = NULL;
+	return -1;
     }
 
     if (listen(vs->lsock, 1) == -1) {
 	fprintf(stderr, "listen() failed\n");
-	exit(1);
-    }
-
-    ret = qemu_set_fd_handler2(vs->lsock, vnc_listen_poll, vnc_listen_read, NULL, vs);
-    if (ret == -1) {
-	exit(1);
-    }
-}
+	close(vs->lsock);
+	vs->lsock = -1;
+	free(vs->display);
+	vs->display = NULL;
+	return -1;
+    }
+
+    return qemu_set_fd_handler2(vs->lsock, vnc_listen_poll, vnc_listen_read, NULL, vs);
+}

-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Qemu-devel] PATCH 2/8: Extend monitor 'change' command for VNC
  2007-07-31 19:23 [Qemu-devel] PATCH 0/8: Authentication support for the VNC server Daniel P. Berrange
  2007-07-31 19:25 ` [Qemu-devel] PATCH 1/8: Refactor VNC server setup API Daniel P. Berrange
@ 2007-07-31 19:25 ` Daniel P. Berrange
  2007-08-01  1:43   ` Anthony Liguori
  2007-07-31 19:26 ` [Qemu-devel] PATCH 3/8: VNC password authentication Daniel P. Berrange
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 19+ messages in thread
From: Daniel P. Berrange @ 2007-07-31 19:25 UTC (permalink / raw)
  To: qemu-devel

This patch extends the QEMU monitor 'change' command so that it can
be used to change the configuration of the VNC server. On the command
line the user can use -vnc none, and then issue the 'change vnc :1'
command later from the monitor. This is utilized in the next patch
to let the monitor fetch a password for the server. The concept could
also be extended to serial & parallel devices allowing changing of
their configuration on the fly.

diff -r e85f07144b6c monitor.c
--- a/monitor.c	Tue Jul 31 10:53:15 2007 -0400
+++ b/monitor.c	Tue Jul 31 10:55:31 2007 -0400
@@ -386,7 +386,7 @@ static void do_eject(int force, const ch
     eject_device(bs, force);
 }
 
-static void do_change(const char *device, const char *filename)
+static void do_change_block(const char *device, const char *filename)
 {
     BlockDriverState *bs;
 
@@ -399,6 +399,21 @@ static void do_change(const char *device
         return;
     bdrv_open(bs, filename, 0);
     qemu_key_check(bs, filename);
+}
+
+static void do_change_vnc(const char *target)
+{
+    if (vnc_display_open(NULL, target) < 0)
+	term_printf("could not start VNC server on %s\n", target);
+}
+
+static void do_change(const char *device, const char *target)
+{
+    if (strcmp(device, "vnc") == 0) {
+	do_change_vnc(target);
+    } else {
+	do_change_block(device, target);
+    }
 }
 
 static void do_screen_dump(const char *filename)
diff -r e85f07144b6c vnc.c
--- a/vnc.c	Tue Jul 31 10:53:15 2007 -0400
+++ b/vnc.c	Tue Jul 31 10:55:06 2007 -0400
@@ -1208,7 +1208,7 @@ void vnc_display_init(DisplayState *ds)
 
 void vnc_display_close(DisplayState *ds)
 {
-    VncState *vs = (VncState *)ds->opaque;
+    VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
 
     if (vs->display) {
 	qemu_free(vs->display);
@@ -1230,7 +1230,7 @@ int vnc_display_open(DisplayState *ds, c
     int reuse_addr, ret;
     socklen_t addrlen;
     const char *p;
-    VncState *vs = (VncState *)ds->opaque;
+    VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
 
     vnc_display_close(ds);
     if (strcmp(arg, "none") == 0)

-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Qemu-devel] PATCH 3/8: VNC password authentication
  2007-07-31 19:23 [Qemu-devel] PATCH 0/8: Authentication support for the VNC server Daniel P. Berrange
  2007-07-31 19:25 ` [Qemu-devel] PATCH 1/8: Refactor VNC server setup API Daniel P. Berrange
  2007-07-31 19:25 ` [Qemu-devel] PATCH 2/8: Extend monitor 'change' command for VNC Daniel P. Berrange
@ 2007-07-31 19:26 ` Daniel P. Berrange
  2007-08-01  1:46   ` Anthony Liguori
  2007-07-31 19:27 ` [Qemu-devel] PATCH 4/8: VeNCrypt basic TLS support Daniel P. Berrange
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 19+ messages in thread
From: Daniel P. Berrange @ 2007-07-31 19:26 UTC (permalink / raw)
  To: qemu-devel

This patch introduces support for VNC protocols upto 3.8 and with
it, support for password based authentication. VNC's password based
authentication is not entirely secure, but it is a standard and the
RFB spec requires that all clients support it. The password can be
provided by using the monitor 'change vnc :1' and it will prompt for
a password to be entered. Passwords have upto 8 letters of context.
Pressing 'enter' without entering any characters disables password
auth in the server. NB, we need a custom copy of d3des here because
VNC uses a 'special' modification of the algorithm. This d3des code
is public domain & in all other VNC servers & clients.

diff -r fccd2d79f407 Makefile.target
--- a/Makefile.target	Tue Jul 31 14:48:18 2007 -0400
+++ b/Makefile.target	Tue Jul 31 14:48:19 2007 -0400
@@ -479,7 +479,7 @@ ifdef CONFIG_SDL
 ifdef CONFIG_SDL
 VL_OBJS+=sdl.o x_keymap.o
 endif
-VL_OBJS+=vnc.o
+VL_OBJS+=vnc.o d3des.o
 ifdef CONFIG_COCOA
 VL_OBJS+=cocoa.o
 COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit
@@ -540,7 +540,7 @@ sdl.o: sdl.c keymaps.c sdl_keysym.h
 sdl.o: sdl.c keymaps.c sdl_keysym.h
 	$(CC) $(CFLAGS) $(CPPFLAGS) $(SDL_CFLAGS) $(BASE_CFLAGS) -c -o $@ $<
 
-vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h
+vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h d3des.c d3des.h
 	$(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
 
 sdlaudio.o: sdlaudio.c
diff -r fccd2d79f407 d3des.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/d3des.c	Tue Jul 31 14:48:19 2007 -0400
@@ -0,0 +1,434 @@
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC.  Also the bytebit[] array
+ * has been reversed so that the most significant bit in each byte of the
+ * key is ignored, not the least significant.
+ *
+ * These changes are:
+ *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* D3DES (V5.09) -
+ *
+ * A portable, public domain, version of the Data Encryption Standard.
+ *
+ * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
+ * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
+ * code;  Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
+ * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
+ * for humouring me on.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
+ * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
+ */
+
+#include "d3des.h"
+
+static void scrunch(unsigned char *, unsigned long *);
+static void unscrun(unsigned long *, unsigned char *);
+static void desfunc(unsigned long *, unsigned long *);
+static void cookey(unsigned long *);
+
+static unsigned long KnL[32] = { 0L };
+
+static unsigned short bytebit[8]	= {
+	01, 02, 04, 010, 020, 040, 0100, 0200 };
+
+static unsigned long bigbyte[24] = {
+	0x800000L,	0x400000L,	0x200000L,	0x100000L,
+	0x80000L,	0x40000L,	0x20000L,	0x10000L,
+	0x8000L,	0x4000L,	0x2000L,	0x1000L,
+	0x800L, 	0x400L, 	0x200L, 	0x100L,
+	0x80L,		0x40L,		0x20L,		0x10L,
+	0x8L,		0x4L,		0x2L,		0x1L	};
+
+/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */
+
+static unsigned char pc1[56] = {
+	56, 48, 40, 32, 24, 16,  8,	 0, 57, 49, 41, 33, 25, 17,
+	 9,  1, 58, 50, 42, 34, 26,	18, 10,  2, 59, 51, 43, 35,
+	62, 54, 46, 38, 30, 22, 14,	 6, 61, 53, 45, 37, 29, 21,
+	13,  5, 60, 52, 44, 36, 28,	20, 12,  4, 27, 19, 11,  3 };
+
+static unsigned char totrot[16] = {
+	1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 };
+
+static unsigned char pc2[48] = {
+	13, 16, 10, 23,  0,  4,  2, 27, 14,  5, 20,  9,
+	22, 18, 11,  3, 25,  7, 15,  6, 26, 19, 12,  1,
+	40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
+	43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 };
+
+void deskey(key, edf)	/* Thanks to James Gillogly & Phil Karn! */
+unsigned char *key;
+int edf;
+{
+	register int i, j, l, m, n;
+	unsigned char pc1m[56], pcr[56];
+	unsigned long kn[32];
+
+	for ( j = 0; j < 56; j++ ) {
+		l = pc1[j];
+		m = l & 07;
+		pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0;
+		}
+	for( i = 0; i < 16; i++ ) {
+		if( edf == DE1 ) m = (15 - i) << 1;
+		else m = i << 1;
+		n = m + 1;
+		kn[m] = kn[n] = 0L;
+		for( j = 0; j < 28; j++ ) {
+			l = j + totrot[i];
+			if( l < 28 ) pcr[j] = pc1m[l];
+			else pcr[j] = pc1m[l - 28];
+			}
+		for( j = 28; j < 56; j++ ) {
+		    l = j + totrot[i];
+		    if( l < 56 ) pcr[j] = pc1m[l];
+		    else pcr[j] = pc1m[l - 28];
+		    }
+		for( j = 0; j < 24; j++ ) {
+			if( pcr[pc2[j]] ) kn[m] |= bigbyte[j];
+			if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j];
+			}
+		}
+	cookey(kn);
+	return;
+	}
+
+static void cookey(raw1)
+register unsigned long *raw1;
+{
+	register unsigned long *cook, *raw0;
+	unsigned long dough[32];
+	register int i;
+
+	cook = dough;
+	for( i = 0; i < 16; i++, raw1++ ) {
+		raw0 = raw1++;
+		*cook	 = (*raw0 & 0x00fc0000L) << 6;
+		*cook	|= (*raw0 & 0x00000fc0L) << 10;
+		*cook	|= (*raw1 & 0x00fc0000L) >> 10;
+		*cook++ |= (*raw1 & 0x00000fc0L) >> 6;
+		*cook	 = (*raw0 & 0x0003f000L) << 12;
+		*cook	|= (*raw0 & 0x0000003fL) << 16;
+		*cook	|= (*raw1 & 0x0003f000L) >> 4;
+		*cook++ |= (*raw1 & 0x0000003fL);
+		}
+	usekey(dough);
+	return;
+	}
+
+void cpkey(into)
+register unsigned long *into;
+{
+	register unsigned long *from, *endp;
+
+	from = KnL, endp = &KnL[32];
+	while( from < endp ) *into++ = *from++;
+	return;
+	}
+
+void usekey(from)
+register unsigned long *from;
+{
+	register unsigned long *to, *endp;
+
+	to = KnL, endp = &KnL[32];
+	while( to < endp ) *to++ = *from++;
+	return;
+	}
+
+void des(inblock, outblock)
+unsigned char *inblock, *outblock;
+{
+	unsigned long work[2];
+
+	scrunch(inblock, work);
+	desfunc(work, KnL);
+	unscrun(work, outblock);
+	return;
+	}
+
+static void scrunch(outof, into)
+register unsigned char *outof;
+register unsigned long *into;
+{
+	*into	 = (*outof++ & 0xffL) << 24;
+	*into	|= (*outof++ & 0xffL) << 16;
+	*into	|= (*outof++ & 0xffL) << 8;
+	*into++ |= (*outof++ & 0xffL);
+	*into	 = (*outof++ & 0xffL) << 24;
+	*into	|= (*outof++ & 0xffL) << 16;
+	*into	|= (*outof++ & 0xffL) << 8;
+	*into	|= (*outof   & 0xffL);
+	return;
+	}
+
+static void unscrun(outof, into)
+register unsigned long *outof;
+register unsigned char *into;
+{
+	*into++ = (unsigned char)((*outof >> 24) & 0xffL);
+	*into++ = (unsigned char)((*outof >> 16) & 0xffL);
+	*into++ = (unsigned char)((*outof >>  8) & 0xffL);
+	*into++ = (unsigned char)(*outof++	 & 0xffL);
+	*into++ = (unsigned char)((*outof >> 24) & 0xffL);
+	*into++ = (unsigned char)((*outof >> 16) & 0xffL);
+	*into++ = (unsigned char)((*outof >>  8) & 0xffL);
+	*into	=  (unsigned char)(*outof	 & 0xffL);
+	return;
+	}
+
+static unsigned long SP1[64] = {
+	0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L,
+	0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L,
+	0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L,
+	0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L,
+	0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L,
+	0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L,
+	0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L,
+	0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L,
+	0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L,
+	0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L,
+	0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L,
+	0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L,
+	0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L,
+	0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L,
+	0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L,
+	0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L };
+
+static unsigned long SP2[64] = {
+	0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L,
+	0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L,
+	0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L,
+	0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L,
+	0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L,
+	0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L,
+	0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L,
+	0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L,
+	0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L,
+	0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L,
+	0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L,
+	0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L,
+	0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L,
+	0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L,
+	0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L,
+	0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L };
+
+static unsigned long SP3[64] = {
+	0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L,
+	0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L,
+	0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L,
+	0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L,
+	0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L,
+	0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L,
+	0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L,
+	0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L,
+	0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L,
+	0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L,
+	0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L,
+	0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L,
+	0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L,
+	0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L,
+	0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L,
+	0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L };
+
+static unsigned long SP4[64] = {
+	0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+	0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L,
+	0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L,
+	0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L,
+	0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L,
+	0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L,
+	0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L,
+	0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L,
+	0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L,
+	0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L,
+	0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L,
+	0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+	0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L,
+	0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L,
+	0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L,
+	0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L };
+
+static unsigned long SP5[64] = {
+	0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L,
+	0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L,
+	0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L,
+	0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L,
+	0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L,
+	0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L,
+	0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L,
+	0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L,
+	0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L,
+	0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L,
+	0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L,
+	0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L,
+	0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L,
+	0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L,
+	0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L,
+	0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L };
+
+static unsigned long SP6[64] = {
+	0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L,
+	0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L,
+	0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L,
+	0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L,
+	0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L,
+	0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L,
+	0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L,
+	0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L,
+	0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L,
+	0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L,
+	0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L,
+	0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L,
+	0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L,
+	0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L,
+	0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L,
+	0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L };
+
+static unsigned long SP7[64] = {
+	0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L,
+	0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L,
+	0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L,
+	0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L,
+	0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L,
+	0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L,
+	0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L,
+	0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L,
+	0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L,
+	0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L,
+	0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L,
+	0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L,
+	0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L,
+	0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L,
+	0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L,
+	0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L };
+
+static unsigned long SP8[64] = {
+	0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L,
+	0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L,
+	0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L,
+	0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L,
+	0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L,
+	0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L,
+	0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L,
+	0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L,
+	0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L,
+	0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L,
+	0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L,
+	0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L,
+	0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L,
+	0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L,
+	0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L,
+	0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L };
+
+static void desfunc(block, keys)
+register unsigned long *block, *keys;
+{
+	register unsigned long fval, work, right, leftt;
+	register int round;
+
+	leftt = block[0];
+	right = block[1];
+	work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL;
+	right ^= work;
+	leftt ^= (work << 4);
+	work = ((leftt >> 16) ^ right) & 0x0000ffffL;
+	right ^= work;
+	leftt ^= (work << 16);
+	work = ((right >> 2) ^ leftt) & 0x33333333L;
+	leftt ^= work;
+	right ^= (work << 2);
+	work = ((right >> 8) ^ leftt) & 0x00ff00ffL;
+	leftt ^= work;
+	right ^= (work << 8);
+	right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL;
+	work = (leftt ^ right) & 0xaaaaaaaaL;
+	leftt ^= work;
+	right ^= work;
+	leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL;
+
+	for( round = 0; round < 8; round++ ) {
+		work  = (right << 28) | (right >> 4);
+		work ^= *keys++;
+		fval  = SP7[ work		 & 0x3fL];
+		fval |= SP5[(work >>  8) & 0x3fL];
+		fval |= SP3[(work >> 16) & 0x3fL];
+		fval |= SP1[(work >> 24) & 0x3fL];
+		work  = right ^ *keys++;
+		fval |= SP8[ work		 & 0x3fL];
+		fval |= SP6[(work >>  8) & 0x3fL];
+		fval |= SP4[(work >> 16) & 0x3fL];
+		fval |= SP2[(work >> 24) & 0x3fL];
+		leftt ^= fval;
+		work  = (leftt << 28) | (leftt >> 4);
+		work ^= *keys++;
+		fval  = SP7[ work		 & 0x3fL];
+		fval |= SP5[(work >>  8) & 0x3fL];
+		fval |= SP3[(work >> 16) & 0x3fL];
+		fval |= SP1[(work >> 24) & 0x3fL];
+		work  = leftt ^ *keys++;
+		fval |= SP8[ work		 & 0x3fL];
+		fval |= SP6[(work >>  8) & 0x3fL];
+		fval |= SP4[(work >> 16) & 0x3fL];
+		fval |= SP2[(work >> 24) & 0x3fL];
+		right ^= fval;
+		}
+
+	right = (right << 31) | (right >> 1);
+	work = (leftt ^ right) & 0xaaaaaaaaL;
+	leftt ^= work;
+	right ^= work;
+	leftt = (leftt << 31) | (leftt >> 1);
+	work = ((leftt >> 8) ^ right) & 0x00ff00ffL;
+	right ^= work;
+	leftt ^= (work << 8);
+	work = ((leftt >> 2) ^ right) & 0x33333333L;
+	right ^= work;
+	leftt ^= (work << 2);
+	work = ((right >> 16) ^ leftt) & 0x0000ffffL;
+	leftt ^= work;
+	right ^= (work << 16);
+	work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL;
+	leftt ^= work;
+	right ^= (work << 4);
+	*block++ = right;
+	*block = leftt;
+	return;
+	}
+
+/* Validation sets:
+ *
+ * Single-length key, single-length plaintext -
+ * Key	  : 0123 4567 89ab cdef
+ * Plain  : 0123 4567 89ab cde7
+ * Cipher : c957 4425 6a5e d31d
+ *
+ * Double-length key, single-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain  : 0123 4567 89ab cde7
+ * Cipher : 7f1d 0a77 826b 8aff
+ *
+ * Double-length key, double-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain  : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7
+ *
+ * Triple-length key, single-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain  : 0123 4567 89ab cde7
+ * Cipher : de0b 7c06 ae5e 0ed5
+ *
+ * Triple-length key, double-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain  : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5
+ *
+ * d3des V5.0a rwo 9208.07 18:44 Graven Imagery
+ **********************************************************************/
diff -r fccd2d79f407 d3des.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/d3des.h	Tue Jul 31 14:48:19 2007 -0400
@@ -0,0 +1,51 @@
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC.
+ *
+ * These changes are:
+ *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* d3des.h -
+ *
+ *	Headers and defines for d3des.c
+ *	Graven Imagery, 1992.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge
+ *	(GEnie : OUTER; CIS : [71755,204])
+ */
+
+#define EN0	0	/* MODE == encrypt */
+#define DE1	1	/* MODE == decrypt */
+
+extern void deskey(unsigned char *, int);
+/*		      hexkey[8]     MODE
+ * Sets the internal key register according to the hexadecimal
+ * key contained in the 8 bytes of hexkey, according to the DES,
+ * for encryption or decryption according to MODE.
+ */
+
+extern void usekey(unsigned long *);
+/*		    cookedkey[32]
+ * Loads the internal key register with the data in cookedkey.
+ */
+
+extern void cpkey(unsigned long *);
+/*		   cookedkey[32]
+ * Copies the contents of the internal key register into the storage
+ * located at &cookedkey[0].
+ */
+
+extern void des(unsigned char *, unsigned char *);
+/*		    from[8]	      to[8]
+ * Encrypts/Decrypts (according to the key currently loaded in the
+ * internal key register) one block of eight bytes at address 'from'
+ * into the block at address 'to'.  They can be the same.
+ */
+
+/* d3des.h V5.09 rwo 9208.04 15:06 Graven Imagery
+ ********************************************************************/
diff -r fccd2d79f407 monitor.c
--- a/monitor.c	Tue Jul 31 14:48:18 2007 -0400
+++ b/monitor.c	Tue Jul 31 14:48:19 2007 -0400
@@ -403,7 +403,10 @@ static void do_change_block(const char *
 
 static void do_change_vnc(const char *target)
 {
-    if (vnc_display_open(NULL, target) < 0)
+    char password[9];
+    monitor_readline("Password: ", 1, password, sizeof(password)-1);
+    password[sizeof(password)-1] = '\0';
+    if (vnc_display_open(NULL, target, password) < 0)
 	term_printf("could not start VNC server on %s\n", target);
 }
 
diff -r fccd2d79f407 vl.c
--- a/vl.c	Tue Jul 31 14:48:18 2007 -0400
+++ b/vl.c	Tue Jul 31 14:48:19 2007 -0400
@@ -7945,7 +7945,7 @@ int main(int argc, char **argv)
         dumb_display_init(ds);
     } else if (vnc_display != NULL) {
         vnc_display_init(ds);
-        if (vnc_display_open(ds, vnc_display) < 0)
+        if (vnc_display_open(ds, vnc_display, NULL) < 0)
             exit(1);
     } else {
 #if defined(CONFIG_SDL)
diff -r fccd2d79f407 vl.h
--- a/vl.h	Tue Jul 31 14:48:18 2007 -0400
+++ b/vl.h	Tue Jul 31 14:48:19 2007 -0400
@@ -969,7 +969,7 @@ void cocoa_display_init(DisplayState *ds
 /* vnc.c */
 void vnc_display_init(DisplayState *ds);
 void vnc_display_close(DisplayState *ds);
-int vnc_display_open(DisplayState *ds, const char *display);
+int vnc_display_open(DisplayState *ds, const char *display, const char *password);
 void do_info_vnc(void);
 
 /* x_keymap.c */
diff -r fccd2d79f407 vnc.c
--- a/vnc.c	Tue Jul 31 14:48:18 2007 -0400
+++ b/vnc.c	Tue Jul 31 14:48:19 2007 -0400
@@ -30,6 +30,15 @@
 
 #include "vnc_keysym.h"
 #include "keymaps.c"
+#include "d3des.h"
+
+// #define _VNC_DEBUG
+
+#ifdef _VNC_DEBUG
+#define VNC_DEBUG(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define VNC_DEBUG(fmt, ...) do { } while (0)
+#endif
 
 typedef struct Buffer
 {
@@ -53,6 +62,20 @@ typedef void VncSendHextileTile(VncState
 #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
+
+enum {
+    VNC_AUTH_INVALID = 0,
+    VNC_AUTH_NONE = 1,
+    VNC_AUTH_VNC = 2,
+    VNC_AUTH_RA2 = 5,
+    VNC_AUTH_RA2NE = 6,
+    VNC_AUTH_TIGHT = 16,
+    VNC_AUTH_ULTRA = 17,
+    VNC_AUTH_TLS = 18,
+    VNC_AUTH_VENCRYPT = 19
+};
 
 struct VncState
 {
@@ -73,7 +96,13 @@ struct VncState
     int last_x;
     int last_y;
 
+    int major;
+    int minor;
+
     char *display;
+    char *password;
+    int auth;
+    char challenge[VNC_AUTH_CHALLENGE_SIZE];
 
     Buffer output;
     Buffer input;
@@ -1125,23 +1154,152 @@ static int protocol_client_init(VncState
     return 0;
 }
 
+static void make_challenge(VncState *vs)
+{
+    int i;
+
+    srand(time(NULL)+getpid()+getpid()*987654+rand());
+
+    for (i = 0 ; i < sizeof(vs->challenge) ; i++)
+        vs->challenge[i] = (int) (256.0*rand()/(RAND_MAX+1.0));
+}
+
+static int protocol_client_auth_vnc(VncState *vs, char *data, size_t len)
+{
+    char response[VNC_AUTH_CHALLENGE_SIZE];
+    int i, j, pwlen;
+    char key[8];
+
+    memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE);
+
+    /* Calculate the expected challenge response */
+    pwlen = strlen(vs->password);
+    for (i=0; i<sizeof(key); i++)
+        key[i] = i<pwlen ? vs->password[i] : 0;
+    deskey(key, EN0);
+    for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8)
+        des(response+j, response+j);
+
+    /* Compare expected vs actual challenge response */
+    if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) {
+	VNC_DEBUG("Client challenge reponse did not match\n");
+	vnc_write_u32(vs, 1); /* Reject auth */
+	if (vs->minor >= 8) {
+	    static const char err[] = "Authentication failed";
+	    vnc_write_u32(vs, sizeof(err));
+	    vnc_write(vs, err, sizeof(err));
+	}
+	vnc_flush(vs);
+	vnc_client_error(vs);
+    } else {
+	VNC_DEBUG("Accepting VNC challenge response\n");
+	vnc_write_u32(vs, 0); /* Accept auth */
+	vnc_flush(vs);
+
+	vnc_read_when(vs, protocol_client_init, 1);
+    }
+    return 0;
+}
+
+static int start_auth_vnc(VncState *vs)
+{
+    make_challenge(vs);
+    /* Send client a 'random' challenge */
+    vnc_write(vs, vs->challenge, sizeof(vs->challenge));
+    vnc_flush(vs);
+
+    vnc_read_when(vs, protocol_client_auth_vnc, sizeof(vs->challenge));
+    return 0;
+}
+
+static int protocol_client_auth(VncState *vs, char *data, size_t len)
+{
+    /* We only advertise 1 auth scheme at a time, so client
+     * must pick the one we sent. Verify this */
+    if (data[0] != vs->auth) { /* Reject auth */
+       VNC_DEBUG("Reject auth %d\n", (int)data[0]);
+       vnc_write_u32(vs, 1);
+       if (vs->minor >= 8) {
+           static const char err[] = "Authentication failed";
+           vnc_write_u32(vs, sizeof(err));
+           vnc_write(vs, err, sizeof(err));
+       }
+       vnc_client_error(vs);
+    } else { /* Accept requested auth */
+       VNC_DEBUG("Client requested auth %d\n", (int)data[0]);
+       switch (vs->auth) {
+       case VNC_AUTH_NONE:
+           VNC_DEBUG("Accept auth none\n");
+           vnc_write_u32(vs, 0); /* Accept auth completion */
+           vnc_read_when(vs, protocol_client_init, 1);
+           break;
+
+       case VNC_AUTH_VNC:
+           VNC_DEBUG("Start VNC auth\n");
+           return start_auth_vnc(vs);
+
+       default: /* Should not be possible, but just in case */
+           VNC_DEBUG("Reject auth %d\n", vs->auth);
+           vnc_write_u8(vs, 1);
+           if (vs->minor >= 8) {
+               static const char err[] = "Authentication failed";
+               vnc_write_u32(vs, sizeof(err));
+               vnc_write(vs, err, sizeof(err));
+           }
+           vnc_client_error(vs);
+       }
+    }
+    return 0;
+}
+
 static int protocol_version(VncState *vs, char *version, size_t len)
 {
     char local[13];
-    int maj, min;
 
     memcpy(local, version, 12);
     local[12] = 0;
 
-    if (sscanf(local, "RFB %03d.%03d\n", &maj, &min) != 2) {
+    if (sscanf(local, "RFB %03d.%03d\n", &vs->major, &vs->minor) != 2) {
+	VNC_DEBUG("Malformed protocol version %s\n", local);
 	vnc_client_error(vs);
 	return 0;
     }
-
-    vnc_write_u32(vs, 1); /* None */
-    vnc_flush(vs);
-
-    vnc_read_when(vs, protocol_client_init, 1);
+    VNC_DEBUG("Client request protocol version %d.%d\n", vs->major, vs->minor);
+    if (vs->major != 3 ||
+	(vs->minor != 3 &&
+	 vs->minor != 7 &&
+	 vs->minor != 8)) {
+	VNC_DEBUG("Unsupported client version\n");
+	vnc_write_u32(vs, VNC_AUTH_INVALID);
+	vnc_flush(vs);
+	vnc_client_error(vs);
+	return 0;
+    }
+
+    if (vs->minor == 3) {
+	if (vs->auth == VNC_AUTH_NONE) {
+            VNC_DEBUG("Tell client auth none\n");
+            vnc_write_u32(vs, vs->auth);
+            vnc_flush(vs);
+            vnc_read_when(vs, protocol_client_init, 1);
+       } else if (vs->auth == VNC_AUTH_VNC) {
+            VNC_DEBUG("Tell client VNC auth\n");
+            vnc_write_u32(vs, vs->auth);
+            vnc_flush(vs);
+            start_auth_vnc(vs);
+       } else {
+            VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->auth);
+            vnc_write_u32(vs, VNC_AUTH_INVALID);
+            vnc_flush(vs);
+            vnc_client_error(vs);
+       }
+    } else {
+	VNC_DEBUG("Telling client we support auth %d\n", vs->auth);
+	vnc_write_u8(vs, 1); /* num auth */
+	vnc_write_u8(vs, vs->auth);
+	vnc_read_when(vs, protocol_client_auth, 1);
+	vnc_flush(vs);
+    }
 
     return 0;
 }
@@ -1156,7 +1314,7 @@ static void vnc_listen_read(void *opaque
     if (vs->csock != -1) {
         socket_set_nonblock(vs->csock);
 	qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, opaque);
-	vnc_write(vs, "RFB 003.003\n", 12);
+	vnc_write(vs, "RFB 003.008\n", 12);
 	vnc_flush(vs);
 	vnc_read_when(vs, protocol_version, 12);
 	memset(vs->old_data, 0, vs->ds->linesize * vs->ds->height);
@@ -1180,6 +1338,7 @@ void vnc_display_init(DisplayState *ds)
     ds->opaque = vs;
     vnc_state = vs;
     vs->display = NULL;
+    vs->password = NULL;
 
     vs->lsock = -1;
     vs->csock = -1;
@@ -1214,13 +1373,18 @@ void vnc_display_close(DisplayState *ds)
 	qemu_free(vs->display);
 	vs->display = NULL;
     }
+    if (vs->password) {
+	qemu_free(vs->password);
+	vs->password = NULL;
+    }
+    vs->auth = VNC_AUTH_INVALID;
     if (vs->lsock != -1) {
 	close(vs->lsock);
 	vs->lsock = -1;
     }
 }
 
-int vnc_display_open(DisplayState *ds, const char *arg)
+int vnc_display_open(DisplayState *ds, const char *display, const char *password)
 {
     struct sockaddr *addr;
     struct sockaddr_in iaddr;
@@ -1233,13 +1397,25 @@ int vnc_display_open(DisplayState *ds, c
     VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
 
     vnc_display_close(ds);
-    if (strcmp(arg, "none") == 0)
+    if (strcmp(display, "none") == 0)
 	return 0;
 
-    if (!(vs->display = strdup(arg)))
+    if (!(vs->display = strdup(display)))
 	return -1;
+    if (password && password[0]) {
+	if (!(vs->password = qemu_strdup(password))) {
+	    qemu_free(vs->display);
+	    vs->display = NULL;
+	    return -1;
+	}
+	VNC_DEBUG("Initializing VNC server with password auth\n");
+	vs->auth = VNC_AUTH_VNC;
+    } else {
+	VNC_DEBUG("Initializing VNC server with no auth\n");
+	vs->auth = VNC_AUTH_NONE;
+    }
 #ifndef _WIN32
-    if (strstart(arg, "unix:", &p)) {
+    if (strstart(display, "unix:", &p)) {
 	addr = (struct sockaddr *)&uaddr;
 	addrlen = sizeof(uaddr);
 
@@ -1262,7 +1438,7 @@ int vnc_display_open(DisplayState *ds, c
 	addr = (struct sockaddr *)&iaddr;
 	addrlen = sizeof(iaddr);
 
-	if (parse_host_port(&iaddr, arg) < 0) {
+	if (parse_host_port(&iaddr, display) < 0) {
 	    fprintf(stderr, "Could not parse VNC address\n");
 	    free(vs->display);
 	    vs->display = NULL;

-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Qemu-devel] PATCH 4/8: VeNCrypt basic TLS support
  2007-07-31 19:23 [Qemu-devel] PATCH 0/8: Authentication support for the VNC server Daniel P. Berrange
                   ` (2 preceding siblings ...)
  2007-07-31 19:26 ` [Qemu-devel] PATCH 3/8: VNC password authentication Daniel P. Berrange
@ 2007-07-31 19:27 ` Daniel P. Berrange
  2007-08-01  1:50   ` Anthony Liguori
  2007-07-31 19:28 ` [Qemu-devel] PATCH 5/8: x509 certificate for server Daniel P. Berrange
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 19+ messages in thread
From: Daniel P. Berrange @ 2007-07-31 19:27 UTC (permalink / raw)
  To: qemu-devel

This patch introduces minimal support for the VeNCrypt protocol
extension. This layers use of TLS (aka SSL) into the VNC data stream,
providing session encryption. This patch is the bare minimum protocol
support. It is enabled by using the 'tls' option flag eg "-vnc :1,tls'
This is not secure on its own since it uses anonymous credentials.
The next patches will introduce x509 certificate credentials.

The configure script is setup to that TLS is only compiled in if the
--enable-vnc-tls flag is provided. This should avoid any breakage on
platforms without the GNU TLS libraries.

diff -r a1fa771c6cf9 Makefile.target
--- a/Makefile.target	Tue Jul 31 14:50:01 2007 -0400
+++ b/Makefile.target	Tue Jul 31 14:50:03 2007 -0400
@@ -402,6 +402,11 @@ endif
 endif
 AUDIODRV+= wavcapture.o
 
+ifdef CONFIG_VNC_TLS
+CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS)
+LIBS += $(CONFIG_VNC_TLS_LIBS)
+endif
+
 VL_OBJS += i2c.o smbus.o
 
 # SCSI layer
diff -r a1fa771c6cf9 configure
--- a/configure	Tue Jul 31 14:50:01 2007 -0400
+++ b/configure	Tue Jul 31 14:50:03 2007 -0400
@@ -89,6 +89,7 @@ fmod="no"
 fmod="no"
 fmod_lib=""
 fmod_inc=""
+vnc_tls="no"
 bsd="no"
 linux="no"
 kqemu="no"
@@ -252,6 +253,8 @@ for opt do
   ;;
   --fmod-inc=*) fmod_inc="$optarg"
   ;;
+  --enable-vnc-tls) vnc_tls="yes"
+  ;;
   --enable-mingw32) mingw32="yes" ; cross_prefix="i386-mingw32-" ; linux_user="no"
   ;;
   --disable-slirp) slirp="no"
@@ -362,6 +365,7 @@ echo "  --enable-alsa            enable 
 echo "  --enable-alsa            enable ALSA audio driver"
 echo "  --enable-fmod            enable FMOD audio driver"
 echo "  --enable-dsound          enable DirectSound audio driver"
+echo "  --enable-vnc-tls         enable TLS encryption for VNC server"
 echo "  --enable-system          enable all system emulation targets"
 echo "  --disable-system         disable all system emulation targets"
 echo "  --enable-linux-user      enable all linux usermode emulation targets"
@@ -589,6 +593,16 @@ fi # -z $sdl
 fi # -z $sdl
 
 ##########################################
+# VNC TLS detection
+if test "$vnc_tls" = "yes" ; then
+  `pkg-config gnutls` || vnc_tls="no"
+fi
+if test "$vnc_tls" = "yes" ; then
+  vnc_tls_cflags=`pkg-config --cflags gnutls`
+  vnc_tls_libs=`pkg-config --libs gnutls`
+fi
+
+##########################################
 # alsa sound support libraries
 
 if test "$alsa" = "yes" ; then
@@ -675,6 +689,11 @@ fi
 fi
 echo "FMOD support      $fmod $fmod_support"
 echo "OSS support       $oss"
+echo "VNC TLS support   $vnc_tls"
+if test "$vnc_tls" = "yes" ; then
+    echo "    TLS CFLAGS    $vnc_tls_cflags"
+    echo "    TLS LIBS      $vnc_tls_libs"
+fi
 if test -n "$sparc_cpu"; then
     echo "Target Sparc Arch $sparc_cpu"
 fi
@@ -845,6 +864,12 @@ if test "$fmod" = "yes" ; then
   echo "CONFIG_FMOD_INC=$fmod_inc" >> $config_mak
   echo "#define CONFIG_FMOD 1" >> $config_h
 fi
+if test "$vnc_tls" = "yes" ; then
+  echo "CONFIG_VNC_TLS=yes" >> $config_mak
+  echo "CONFIG_VNC_TLS_CFLAGS=$vnc_tls_cflags" >> $config_mak
+  echo "CONFIG_VNC_TLS_LIBS=$vnc_tls_libs" >> $config_mak
+  echo "#define CONFIG_VNC_TLS 1" >> $config_h
+fi
 qemu_version=`head $source_path/VERSION`
 echo "VERSION=$qemu_version" >>$config_mak
 echo "#define QEMU_VERSION \"$qemu_version\"" >> $config_h
diff -r a1fa771c6cf9 vl.c
--- a/vl.c	Tue Jul 31 14:50:01 2007 -0400
+++ b/vl.c	Tue Jul 31 14:50:03 2007 -0400
@@ -6458,7 +6458,7 @@ void main_loop_wait(int timeout)
             if (FD_ISSET(ioh->fd, &rfds)) {
                 ioh->fd_read(ioh->opaque);
             }
-            if (FD_ISSET(ioh->fd, &wfds)) {
+            if (!ioh->deleted && ioh->fd_write && FD_ISSET(ioh->fd, &wfds)) {
                 ioh->fd_write(ioh->opaque);
             }
         }
diff -r a1fa771c6cf9 vnc.c
--- a/vnc.c	Tue Jul 31 14:50:01 2007 -0400
+++ b/vnc.c	Tue Jul 31 14:50:03 2007 -0400
@@ -32,13 +32,25 @@
 #include "keymaps.c"
 #include "d3des.h"
 
-// #define _VNC_DEBUG
-
-#ifdef _VNC_DEBUG
+#if CONFIG_VNC_TLS
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#endif /* CONFIG_VNC_TLS */
+
+// #define _VNC_DEBUG 1
+
+#if _VNC_DEBUG
 #define VNC_DEBUG(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+
+#if CONFIG_VNC_TLS && _VNC_DEBUG == 2
+static void debug_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
+
 
 typedef struct Buffer
 {
@@ -76,6 +88,23 @@ enum {
     VNC_AUTH_TLS = 18,
     VNC_AUTH_VENCRYPT = 19
 };
+
+#if CONFIG_VNC_TLS
+enum {
+    VNC_WIREMODE_CLEAR,
+    VNC_WIREMODE_TLS,
+};
+
+enum {
+    VNC_AUTH_VENCRYPT_PLAIN = 256,
+    VNC_AUTH_VENCRYPT_TLSNONE = 257,
+    VNC_AUTH_VENCRYPT_TLSVNC = 258,
+    VNC_AUTH_VENCRYPT_TLSPLAIN = 259,
+    VNC_AUTH_VENCRYPT_X509NONE = 260,
+    VNC_AUTH_VENCRYPT_X509VNC = 261,
+    VNC_AUTH_VENCRYPT_X509PLAIN = 262,
+};
+#endif /* CONFIG_VNC_TLS */
 
 struct VncState
 {
@@ -102,7 +131,15 @@ struct VncState
     char *display;
     char *password;
     int auth;
+#if CONFIG_VNC_TLS
+    int subauth;
+#endif
     char challenge[VNC_AUTH_CHALLENGE_SIZE];
+
+#if CONFIG_VNC_TLS
+    int wiremode;
+    gnutls_session_t tls_session;
+#endif
 
     Buffer output;
     Buffer input;
@@ -579,12 +616,20 @@ static int vnc_client_io_error(VncState 
 	if (ret == -1 && (last_errno == EINTR || last_errno == EAGAIN))
 	    return 0;
 
+	VNC_DEBUG("Closing down client sock %d %d\n", ret, ret < 0 ? last_errno : 0);
 	qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
 	closesocket(vs->csock);
 	vs->csock = -1;
 	buffer_reset(&vs->input);
 	buffer_reset(&vs->output);
 	vs->need_update = 0;
+#if CONFIG_VNC_TLS
+	if (vs->tls_session) {
+	    gnutls_deinit(vs->tls_session);
+	    vs->tls_session = NULL;
+	}
+	vs->wiremode = VNC_WIREMODE_CLEAR;
+#endif /* CONFIG_VNC_TLS */
 	return 0;
     }
     return ret;
@@ -600,7 +645,19 @@ static void vnc_client_write(void *opaqu
     long ret;
     VncState *vs = opaque;
 
-    ret = send(vs->csock, vs->output.buffer, vs->output.offset, 0);
+#if CONFIG_VNC_TLS
+    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;
+	    else
+		errno = EIO;
+	    ret = -1;
+	}
+    } 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());
     if (!ret)
 	return;
@@ -626,7 +683,19 @@ static void vnc_client_read(void *opaque
 
     buffer_reserve(&vs->input, 4096);
 
-    ret = recv(vs->csock, buffer_end(&vs->input), 4096, 0);
+#if CONFIG_VNC_TLS
+    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;
+	    else
+		errno = EIO;
+	    ret = -1;
+	}
+    } 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;
@@ -720,6 +789,47 @@ static uint32_t read_u32(uint8_t *data, 
     return ((data[offset] << 24) | (data[offset + 1] << 16) |
 	    (data[offset + 2] << 8) | data[offset + 3]);
 }
+
+#if CONFIG_VNC_TLS
+ssize_t vnc_tls_push(gnutls_transport_ptr_t transport,
+		     const void *data,
+		     size_t len) {
+    struct VncState *vs = (struct VncState *)transport;
+    int ret, lastErrno;
+
+ retry:
+    ret = send(vs->csock, data, len, 0);
+    lastErrno = errno;
+    VNC_DEBUG("Send %d errno %d\n", ret, ret < 0 ? lastErrno : 0);
+    if (ret < 0) {
+	if (lastErrno == EINTR)
+	    goto retry;
+	errno = lastErrno;
+	return -1;
+    }
+    return ret;
+}
+
+
+ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport,
+		     void *data,
+		     size_t len) {
+    struct VncState *vs = (struct VncState *)transport;
+    int ret, lastErrno;
+
+ retry:
+    ret = recv(vs->csock, data, len, 0);
+    lastErrno = errno;
+    VNC_DEBUG("Recv %d errno %d\n", ret, ret < 0 ? lastErrno : 0);
+    if (ret < 0) {
+	if (lastErrno == EINTR)
+	    goto retry;
+	errno = lastErrno;
+	return -1;
+    }
+    return ret;
+}
+#endif /* CONFIG_VNC_TLS */
 
 static void client_cut_text(VncState *vs, size_t len, char *text)
 {
@@ -1212,6 +1322,243 @@ static int start_auth_vnc(VncState *vs)
     return 0;
 }
 
+
+#if 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 _VNC_DEBUG == 2
+    gnutls_global_set_log_level(10);
+    gnutls_global_set_log_function(debug_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 int start_auth_vencrypt_subauth(VncState *vs)
+{
+    switch (vs->subauth) {
+    case VNC_AUTH_VENCRYPT_TLSNONE:
+       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:
+       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->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;
+    }
+
+    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);
+}
+
+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};
+    gnutls_anon_server_credentials anon_cred = NULL;
+
+    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, 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;
+	}
+
+	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, char *data, size_t len)
+{
+    int auth = read_u32(data, 0);
+
+    if (auth != vs->subauth) {
+	VNC_DEBUG("Rejecting auth %d\n", auth);
+	vnc_write_u8(vs, 0); /* Reject auth */
+	vnc_flush(vs);
+	vnc_client_error(vs);
+    } else {
+	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;
+	}
+
+	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;
+}
+
+static int protocol_client_vencrypt_init(VncState *vs, char *data, size_t len)
+{
+    if (data[0] != 0 ||
+	data[1] != 2) {
+	VNC_DEBUG("Unsupported VeNCrypt protocol %d.%d\n", (int)data[0], (int)data[1]);
+	vnc_write_u8(vs, 1); /* Reject version */
+	vnc_flush(vs);
+	vnc_client_error(vs);
+    } else {
+	VNC_DEBUG("Sending allowed auth %d\n", vs->subauth);
+	vnc_write_u8(vs, 0); /* Accept version */
+	vnc_write_u8(vs, 1); /* Number of sub-auths */
+	vnc_write_u32(vs, vs->subauth); /* The supported auth */
+	vnc_flush(vs);
+	vnc_read_when(vs, protocol_client_vencrypt_auth, 4);
+    }
+    return 0;
+}
+
+static int start_auth_vencrypt(VncState *vs)
+{
+    /* Send VeNCrypt version 0.2 */
+    vnc_write_u8(vs, 0);
+    vnc_write_u8(vs, 2);
+
+    vnc_read_when(vs, protocol_client_vencrypt_init, 2);
+    return 0;
+}
+#endif /* CONFIG_VNC_TLS */
+
 static int protocol_client_auth(VncState *vs, char *data, size_t len)
 {
     /* We only advertise 1 auth scheme at a time, so client
@@ -1237,6 +1584,12 @@ static int protocol_client_auth(VncState
        case VNC_AUTH_VNC:
            VNC_DEBUG("Start VNC auth\n");
            return start_auth_vnc(vs);
+
+#if CONFIG_VNC_TLS
+       case VNC_AUTH_VENCRYPT:
+           VNC_DEBUG("Accept VeNCrypt auth\n");;
+           return start_auth_vencrypt(vs);
+#endif /* CONFIG_VNC_TLS */
 
        default: /* Should not be possible, but just in case */
            VNC_DEBUG("Reject auth %d\n", vs->auth);
@@ -1312,6 +1665,7 @@ static void vnc_listen_read(void *opaque
 
     vs->csock = accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
     if (vs->csock != -1) {
+	VNC_DEBUG("New client on socket %d\n", vs->csock);
         socket_set_nonblock(vs->csock);
 	qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, opaque);
 	vnc_write(vs, "RFB 003.008\n", 12);
@@ -1378,6 +1732,9 @@ void vnc_display_close(DisplayState *ds)
 	vs->password = NULL;
     }
     vs->auth = VNC_AUTH_INVALID;
+#if CONFIG_VNC_TLS
+    vs->subauth = VNC_AUTH_INVALID;
+#endif
     if (vs->lsock != -1) {
 	close(vs->lsock);
 	vs->lsock = -1;
@@ -1395,6 +1752,10 @@ int vnc_display_open(DisplayState *ds, c
     socklen_t addrlen;
     const char *p;
     VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
+#if CONFIG_VNC_TLS
+    const char *options;
+    int tls = 0;
+#endif
 
     vnc_display_close(ds);
     if (strcmp(display, "none") == 0)
@@ -1402,18 +1763,51 @@ int vnc_display_open(DisplayState *ds, c
 
     if (!(vs->display = strdup(display)))
 	return -1;
+
+#if CONFIG_VNC_TLS
+    options = display;
+    while ((options = strchr(options, ','))) {
+	options++;
+	if (strncmp(options, "tls", 3) == 0)
+	    tls = 1; /* Require TLS */
+    }
+#endif
+
     if (password && password[0]) {
 	if (!(vs->password = qemu_strdup(password))) {
 	    qemu_free(vs->display);
 	    vs->display = NULL;
 	    return -1;
 	}
-	VNC_DEBUG("Initializing VNC server with password auth\n");
-	vs->auth = VNC_AUTH_VNC;
+#if CONFIG_VNC_TLS
+	if (tls) {
+	    VNC_DEBUG("Initializing VNC server with TLS password auth\n");
+	    vs->auth = VNC_AUTH_VENCRYPT;
+	    vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
+	} else {
+#endif
+	    VNC_DEBUG("Initializing VNC server with password auth\n");
+	    vs->auth = VNC_AUTH_VNC;
+#if CONFIG_VNC_TLS
+	    vs->subauth = VNC_AUTH_INVALID;
+	}
+#endif
     } else {
-	VNC_DEBUG("Initializing VNC server with no auth\n");
-	vs->auth = VNC_AUTH_NONE;
-    }
+#if CONFIG_VNC_TLS
+	if (tls) {
+	    VNC_DEBUG("Initializing VNC server with TLS no auth\n");
+	    vs->auth = VNC_AUTH_VENCRYPT;
+	    vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE;
+	} else {
+#endif
+	    VNC_DEBUG("Initializing VNC server with no auth\n");
+	    vs->auth = VNC_AUTH_NONE;
+#if CONFIG_VNC_TLS
+	    vs->subauth = VNC_AUTH_INVALID;
+	}
+#endif
+    }
+
 #ifndef _WIN32
     if (strstart(display, "unix:", &p)) {
 	addr = (struct sockaddr *)&uaddr;

-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Qemu-devel] PATCH 5/8: x509 certificate for server
  2007-07-31 19:23 [Qemu-devel] PATCH 0/8: Authentication support for the VNC server Daniel P. Berrange
                   ` (3 preceding siblings ...)
  2007-07-31 19:27 ` [Qemu-devel] PATCH 4/8: VeNCrypt basic TLS support Daniel P. Berrange
@ 2007-07-31 19:28 ` Daniel P. Berrange
  2007-07-31 19:28 ` [Qemu-devel] PATCH 6/8: x509 client certificate verification Daniel P. Berrange
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 19+ messages in thread
From: Daniel P. Berrange @ 2007-07-31 19:28 UTC (permalink / raw)
  To: qemu-devel

This patch adds support for using x509 certificates on the server
end. The server needs a CA certificate, and its own certificate and
private key. A CA revocation list is optional. This this patch the
file names are hardcoded. The next-but-one patch will make them
configurable.

The use of x509 certificates is controlled by the 'x509' flag to
the VNC arg, eg '-vnc :1,tls,x509'. This only provides encryption
of the session, no authentication.

diff -r b5430bd9f883 vnc.c
--- a/vnc.c	Tue Jul 31 11:32:19 2007 -0400
+++ b/vnc.c	Tue Jul 31 11:43:59 2007 -0400
@@ -104,6 +104,14 @@ enum {
     VNC_AUTH_VENCRYPT_X509VNC = 261,
     VNC_AUTH_VENCRYPT_X509PLAIN = 262,
 };
+
+#if CONFIG_VNC_TLS
+#define CA_FILE "ca-cert.pem"
+#define CRL_FILE "ca-crl.pem"
+#define KEY_FILE "key.pem"
+#define CERT_FILE "cert.pem"
+#endif
+
 #endif /* CONFIG_VNC_TLS */
 
 struct VncState
@@ -1369,16 +1377,59 @@ static gnutls_anon_server_credentials vn
 }
 
 
+static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(void)
+{
+    gnutls_certificate_credentials_t x509_cred;
+    int ret;
+    struct stat st;
+
+    if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) {
+	VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
+	return NULL;
+    }
+    if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred, CA_FILE, GNUTLS_X509_FMT_PEM)) < 0) {
+	VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret));
+	gnutls_certificate_free_credentials(x509_cred);
+	return NULL;
+    }
+
+    if ((ret = gnutls_certificate_set_x509_key_file (x509_cred, CERT_FILE, KEY_FILE,
+						     GNUTLS_X509_FMT_PEM)) < 0) {
+	VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret));
+	gnutls_certificate_free_credentials(x509_cred);
+	return NULL;
+    }
+
+    if (stat(CRL_FILE, &st) < 0) {
+	if (errno != ENOENT) {
+	    gnutls_certificate_free_credentials(x509_cred);
+	    return NULL;
+	}
+    } else {
+	if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred, CRL_FILE, GNUTLS_X509_FMT_PEM)) < 0) {
+	    VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret));
+	    gnutls_certificate_free_credentials(x509_cred);
+	    return NULL;
+	}
+    }
+
+    gnutls_certificate_set_dh_params (x509_cred, dh_params);
+
+    return x509_cred;
+}
+
 static int start_auth_vencrypt_subauth(VncState *vs)
 {
     switch (vs->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);
 
@@ -1429,11 +1480,17 @@ static void vnc_handshake_io(void *opaqu
     vnc_continue_handshake(vs);
 }
 
+#define NEED_X509_AUTH(vs)			      \
+    ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE ||   \
+     (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC ||    \
+     (vs)->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};
-    gnutls_anon_server_credentials anon_cred = NULL;
+    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) {
@@ -1454,7 +1511,7 @@ static int vnc_start_tls(struct VncState
 	    return -1;
 	}
 
-	if (gnutls_kx_set_priority(vs->tls_session, kx_anon) < 0) {
+	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);
@@ -1475,19 +1532,36 @@ static int vnc_start_tls(struct VncState
 	    return -1;
 	}
 
-	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;
+	if (NEED_X509_AUTH(vs)) {
+	    gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred();
+	    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;
+	    }
+	} 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);
@@ -1754,7 +1828,7 @@ int vnc_display_open(DisplayState *ds, c
     VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
 #if CONFIG_VNC_TLS
     const char *options;
-    int tls = 0;
+    int tls = 0, x509 = 0;
 #endif
 
     vnc_display_close(ds);
@@ -1770,6 +1844,8 @@ int vnc_display_open(DisplayState *ds, c
 	options++;
 	if (strncmp(options, "tls", 3) == 0)
 	    tls = 1; /* Require TLS */
+	else if (strncmp(options, "x509", 4) == 0)
+	    x509 = 1; /* Require x509 certificates */
     }
 #endif
 
@@ -1781,9 +1857,14 @@ int vnc_display_open(DisplayState *ds, c
 	}
 #if CONFIG_VNC_TLS
 	if (tls) {
-	    VNC_DEBUG("Initializing VNC server with TLS password auth\n");
 	    vs->auth = VNC_AUTH_VENCRYPT;
-	    vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
+	    if (x509) {
+		VNC_DEBUG("Initializing VNC server with x509 password auth\n");
+		vs->subauth = VNC_AUTH_VENCRYPT_X509VNC;
+	    } else {
+		VNC_DEBUG("Initializing VNC server with TLS password auth\n");
+		vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
+	    }
 	} else {
 #endif
 	    VNC_DEBUG("Initializing VNC server with password auth\n");
@@ -1795,9 +1876,14 @@ int vnc_display_open(DisplayState *ds, c
     } else {
 #if CONFIG_VNC_TLS
 	if (tls) {
-	    VNC_DEBUG("Initializing VNC server with TLS no auth\n");
 	    vs->auth = VNC_AUTH_VENCRYPT;
-	    vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE;
+	    if (x509) {
+		VNC_DEBUG("Initializing VNC server with x509 no auth\n");
+		vs->subauth = VNC_AUTH_VENCRYPT_X509NONE;
+	    } else {
+		VNC_DEBUG("Initializing VNC server with TLS no auth\n");
+		vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE;
+	    }
 	} else {
 #endif
 	    VNC_DEBUG("Initializing VNC server with no auth\n");

-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Qemu-devel] PATCH 6/8: x509 client certificate verification
  2007-07-31 19:23 [Qemu-devel] PATCH 0/8: Authentication support for the VNC server Daniel P. Berrange
                   ` (4 preceding siblings ...)
  2007-07-31 19:28 ` [Qemu-devel] PATCH 5/8: x509 certificate for server Daniel P. Berrange
@ 2007-07-31 19:28 ` Daniel P. Berrange
  2007-07-31 19:29 ` [Qemu-devel] PATCH 7/8: command line args for x509 cert paths Daniel P. Berrange
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 19+ messages in thread
From: Daniel P. Berrange @ 2007-07-31 19:28 UTC (permalink / raw)
  To: qemu-devel

This patch adds support for requesting and validating client certificates.
In effect the client certificates are being used as the authentication
mechansim, making VNC passwords unccessary, though using a combination of
both is also possible.

The verification of client certificates is enabled using the 'x509verify'
flag to VNC arg eg  '-vnc :1,tls,x509verify'

diff -r f8cd8bd742ee vnc.c
--- a/vnc.c	Tue Jul 31 11:45:22 2007 -0400
+++ b/vnc.c	Tue Jul 31 11:45:30 2007 -0400
@@ -37,7 +37,7 @@
 #include <gnutls/x509.h>
 #endif /* CONFIG_VNC_TLS */
 
-// #define _VNC_DEBUG 1
+ #define _VNC_DEBUG 1
 
 #if _VNC_DEBUG
 #define VNC_DEBUG(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
@@ -141,6 +141,7 @@ struct VncState
     int auth;
 #if CONFIG_VNC_TLS
     int subauth;
+    int x509verify;
 #endif
     char challenge[VNC_AUTH_CHALLENGE_SIZE];
 
@@ -1418,6 +1419,85 @@ static gnutls_certificate_credentials_t 
     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->subauth) {
@@ -1464,6 +1544,16 @@ static int vnc_continue_handshake(struct
        VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
        vnc_client_error(vs);
        return -1;
+    }
+
+    if (vs->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");
@@ -1547,6 +1637,11 @@ static int vnc_start_tls(struct VncState
 		vnc_client_error(vs);
 		return -1;
 	    }
+	    if (vs->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) {
@@ -1808,6 +1903,7 @@ void vnc_display_close(DisplayState *ds)
     vs->auth = VNC_AUTH_INVALID;
 #if CONFIG_VNC_TLS
     vs->subauth = VNC_AUTH_INVALID;
+    vs->x509verify = 0;
 #endif
     if (vs->lsock != -1) {
 	close(vs->lsock);
@@ -1842,10 +1938,14 @@ int vnc_display_open(DisplayState *ds, c
     options = display;
     while ((options = strchr(options, ','))) {
 	options++;
-	if (strncmp(options, "tls", 3) == 0)
+	if (strncmp(options, "tls", 3) == 0) {
 	    tls = 1; /* Require TLS */
-	else if (strncmp(options, "x509", 4) == 0)
+	} else if (strncmp(options, "x509verify", 10) == 0) {
+	    x509 = 1;
+	    vs->x509verify = 1; /* Require x509 certificates & verify client */
+	} else if (strncmp(options, "x509", 4) == 0) {
 	    x509 = 1; /* Require x509 certificates */
+	}
     }
 #endif
 

-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Qemu-devel] PATCH 7/8: command line args for x509 cert paths
  2007-07-31 19:23 [Qemu-devel] PATCH 0/8: Authentication support for the VNC server Daniel P. Berrange
                   ` (5 preceding siblings ...)
  2007-07-31 19:28 ` [Qemu-devel] PATCH 6/8: x509 client certificate verification Daniel P. Berrange
@ 2007-07-31 19:29 ` Daniel P. Berrange
  2007-08-01  1:54   ` Anthony Liguori
  2007-07-31 19:30 ` [Qemu-devel] PATCH 8/8: document all VNC authentication options Daniel P. Berrange
  2007-08-01  1:55 ` [Qemu-devel] PATCH 0/8: Authentication support for the VNC server Anthony Liguori
  8 siblings, 1 reply; 19+ messages in thread
From: Daniel P. Berrange @ 2007-07-31 19:29 UTC (permalink / raw)
  To: qemu-devel

This final code patch adds 4 new command line arguments to QEMU to allow the
certificate files to be specified. The '-x509cacert', '-x509cert' and '-x509key'
parameters are mandatory if the 'x509' or 'x509verify' flags are used when
setting up the VNC server. If the certificates are not provided, all client
authentication attempts will be rejected.

diff -r f38519b13575 vl.c
--- a/vl.c	Tue Jul 31 14:51:31 2007 -0400
+++ b/vl.c	Tue Jul 31 14:51:32 2007 -0400
@@ -6701,6 +6701,12 @@ static void help(int exitcode)
            "-no-reboot      exit instead of rebooting\n"
            "-loadvm file    start right away with a saved state (loadvm in monitor)\n"
 	   "-vnc display    start a VNC server on display\n"
+#if CONFIG_VNC_TLS
+	   "-x509cacert FILE  x509 CA certificate for TLS services\n"
+	   "-x509cacrl FILE   x509 CA certificate revocation list for TLS services\n"
+	   "-x509cert FILE    x509 public certificate for TLS services\n"
+	   "-x509key FILE     x509 private key for TLS services\n"
+#endif
 #ifndef _WIN32
 	   "-daemonize      daemonize QEMU after initializing\n"
 #endif
@@ -6796,6 +6802,12 @@ enum {
     QEMU_OPTION_usbdevice,
     QEMU_OPTION_smp,
     QEMU_OPTION_vnc,
+#if CONFIG_VNC_TLS
+    QEMU_OPTION_x509cacert,
+    QEMU_OPTION_x509cacrl,
+    QEMU_OPTION_x509cert,
+    QEMU_OPTION_x509key,
+#endif
     QEMU_OPTION_no_acpi,
     QEMU_OPTION_no_reboot,
     QEMU_OPTION_show_cursor,
@@ -6889,6 +6901,12 @@ const QEMUOption qemu_options[] = {
     { "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice },
     { "smp", HAS_ARG, QEMU_OPTION_smp },
     { "vnc", HAS_ARG, QEMU_OPTION_vnc },
+#if CONFIG_VNC_TLS
+    { "x509cacert", HAS_ARG, QEMU_OPTION_x509cacert },
+    { "x509cacrl", HAS_ARG, QEMU_OPTION_x509cacrl },
+    { "x509cert", HAS_ARG, QEMU_OPTION_x509cert },
+    { "x509key", HAS_ARG, QEMU_OPTION_x509key },
+#endif
 
     /* temporary options */
     { "usb", 0, QEMU_OPTION_usb },
@@ -7171,6 +7189,9 @@ int main(int argc, char **argv)
     int fds[2];
     const char *pid_file = NULL;
     VLANState *vlan;
+#if CONFIG_VNC_TLS
+    const char *x509cacert = NULL, *x509cacrl = NULL, *x509cert = NULL, *x509key = NULL;
+#endif
 
     LIST_INIT (&vm_change_state_head);
 #ifndef _WIN32
@@ -7648,6 +7669,20 @@ int main(int argc, char **argv)
 	    case QEMU_OPTION_vnc:
 		vnc_display = optarg;
 		break;
+#if CONFIG_VNC_TLS
+	    case QEMU_OPTION_x509cacert:
+		x509cacert = optarg;
+		break;
+	    case QEMU_OPTION_x509cacrl:
+		x509cacrl = optarg;
+		break;
+	    case QEMU_OPTION_x509cert:
+		x509cert = optarg;
+		break;
+	    case QEMU_OPTION_x509key:
+		x509key = optarg;
+		break;
+#endif
             case QEMU_OPTION_no_acpi:
                 acpi_enabled = 0;
                 break;
@@ -7945,6 +7980,10 @@ int main(int argc, char **argv)
         dumb_display_init(ds);
     } else if (vnc_display != NULL) {
         vnc_display_init(ds);
+#if CONFIG_VNC_TLS
+        if (vnc_set_x509_credentials(ds, x509cacert, x509cacrl, x509cert, x509key) < 0)
+            exit(1);
+#endif
         if (vnc_display_open(ds, vnc_display, NULL) < 0)
             exit(1);
     } else {
diff -r f38519b13575 vl.h
--- a/vl.h	Tue Jul 31 14:51:31 2007 -0400
+++ b/vl.h	Tue Jul 31 14:51:32 2007 -0400
@@ -971,6 +971,13 @@ void vnc_display_close(DisplayState *ds)
 void vnc_display_close(DisplayState *ds);
 int vnc_display_open(DisplayState *ds, const char *display, const char *password);
 void do_info_vnc(void);
+#if CONFIG_VNC_TLS
+int vnc_set_x509_credentials(DisplayState *ds,
+			     const char *cacert,
+			     const char *cacrl,
+			     const char *cert,
+			     const char *key);
+#endif
 
 /* x_keymap.c */
 extern uint8_t _translate_keycode(const int key);
diff -r f38519b13575 vnc.c
--- a/vnc.c	Tue Jul 31 14:51:31 2007 -0400
+++ b/vnc.c	Tue Jul 31 14:51:32 2007 -0400
@@ -142,6 +142,11 @@ struct VncState
 #if CONFIG_VNC_TLS
     int subauth;
     int x509verify;
+
+    char *x509cacert;
+    char *x509cacrl;
+    char *x509cert;
+    char *x509key;
 #endif
     char challenge[VNC_AUTH_CHALLENGE_SIZE];
 
@@ -1378,36 +1383,50 @@ static gnutls_anon_server_credentials vn
 }
 
 
-static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(void)
+static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncState *vs)
 {
     gnutls_certificate_credentials_t x509_cred;
     int ret;
-    struct stat st;
+
+    if (!vs->x509cacert) {
+	VNC_DEBUG("No CA x509 certificate specified\n");
+	return NULL;
+    }
+    if (!vs->x509cert) {
+	VNC_DEBUG("No server x509 certificate specified\n");
+	return NULL;
+    }
+    if (!vs->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, CA_FILE, GNUTLS_X509_FMT_PEM)) < 0) {
+    if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
+						      vs->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, CERT_FILE, KEY_FILE,
+    if ((ret = gnutls_certificate_set_x509_key_file (x509_cred,
+						     vs->x509cert,
+						     vs->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 (stat(CRL_FILE, &st) < 0) {
-	if (errno != ENOENT) {
-	    gnutls_certificate_free_credentials(x509_cred);
-	    return NULL;
-	}
-    } else {
-	if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred, CRL_FILE, GNUTLS_X509_FMT_PEM)) < 0) {
+    if (vs->x509cacrl) {
+	if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
+							vs->x509cacrl,
+							GNUTLS_X509_FMT_PEM)) < 0) {
 	    VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret));
 	    gnutls_certificate_free_credentials(x509_cred);
 	    return NULL;
@@ -1623,7 +1642,7 @@ static int vnc_start_tls(struct VncState
 	}
 
 	if (NEED_X509_AUTH(vs)) {
-	    gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred();
+	    gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred(vs);
 	    if (!x509_cred) {
 		gnutls_deinit(vs->tls_session);
 		vs->tls_session = NULL;
@@ -1888,6 +1907,43 @@ void vnc_display_init(DisplayState *ds)
     vnc_dpy_resize(vs->ds, 640, 400);
 }
 
+#if CONFIG_VNC_TLS
+int vnc_set_x509_credentials(DisplayState *ds,
+			     const char *cacert,
+			     const char *cacrl,
+			     const char *cert,
+			     const char *key)
+{
+    VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
+
+    if (vs->x509cacert) {
+	free(vs->x509cacert);
+	vs->x509cacert = NULL;
+    }
+    if (vs->x509cacrl) {
+	free(vs->x509cacrl);
+	vs->x509cacrl = NULL;
+    }
+    if (vs->x509cert) {
+	free(vs->x509cert);
+	vs->x509cert = NULL;
+    }
+    if (vs->x509key) {
+	free(vs->x509key);
+	vs->x509key = NULL;
+    }
+    if (cacert && !(vs->x509cacert = qemu_strdup(cacert)))
+	return -1;
+    if (cacrl && !(vs->x509cacrl = qemu_strdup(cacrl)))
+	return -1;
+    if (cert && !(vs->x509cert = qemu_strdup(cert)))
+	return -1;
+    if (key && !(vs->x509key = qemu_strdup(key)))
+	return -1;
+    return 0;
+}
+#endif /* CONFIG_VNC_TLS */
+
 void vnc_display_close(DisplayState *ds)
 {
     VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;

-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Qemu-devel] PATCH 8/8: document all VNC authentication options
  2007-07-31 19:23 [Qemu-devel] PATCH 0/8: Authentication support for the VNC server Daniel P. Berrange
                   ` (6 preceding siblings ...)
  2007-07-31 19:29 ` [Qemu-devel] PATCH 7/8: command line args for x509 cert paths Daniel P. Berrange
@ 2007-07-31 19:30 ` Daniel P. Berrange
  2007-08-01  1:55 ` [Qemu-devel] PATCH 0/8: Authentication support for the VNC server Anthony Liguori
  8 siblings, 0 replies; 19+ messages in thread
From: Daniel P. Berrange @ 2007-07-31 19:30 UTC (permalink / raw)
  To: qemu-devel

This patch updates the user documentation to detail the new syntax
options for VNC server configuration. It moves all the display
related options into a combined logical section for clarity. It
documents the different deployment secenarios possible with the
new VNC server capabilities and their security. It also provides
a guide for setting up a certificate authority and issuing clients
and server with their certificates

diff -r 9e499876ee0e qemu-doc.texi
--- a/qemu-doc.texi	Tue Jul 31 11:47:13 2007 -0400
+++ b/qemu-doc.texi	Tue Jul 31 14:43:50 2007 -0400
@@ -129,6 +129,7 @@ Download the experimental binary install
 * pcsys_network::      Network emulation
 * direct_linux_boot::  Direct Linux Boot
 * pcsys_usb::          USB emulation
+* vnc_security::       VNC security
 * gdb_usage::          GDB usage
 * pcsys_os_specific::  Target OS specific information
 @end menu
@@ -243,6 +244,56 @@ Simulate an SMP system with @var{n} CPUs
 Simulate an SMP system with @var{n} CPUs. On the PC target, up to 255
 CPUs are supported.
 
+@item -audio-help
+
+Will show the audio subsystem help: list of drivers, tunable
+parameters.
+
+@item -soundhw card1,card2,... or -soundhw all
+
+Enable audio and selected sound hardware. Use ? to print all
+available sound hardware.
+
+@example
+qemu -soundhw sb16,adlib hda
+qemu -soundhw es1370 hda
+qemu -soundhw all hda
+qemu -soundhw ?
+@end example
+
+@item -localtime
+Set the real time clock to local time (the default is to UTC
+time). This option is needed to have correct date in MS-DOS or
+Windows.
+
+@item -pidfile file
+Store the QEMU process PID in @var{file}. It is useful if you launch QEMU
+from a script.
+
+@item -daemonize
+Daemonize the QEMU process after initialization.  QEMU will not detach from
+standard IO until it is ready to receive connections on any of its devices.
+This option is a useful way for external programs to launch QEMU without having
+to cope with initialization race conditions.
+
+@item -win2k-hack
+Use it when installing Windows 2000 to avoid a disk full bug. After
+Windows 2000 is installed, you no longer need this option (this option
+slows down the IDE transfers).
+
+@item -option-rom file
+Load the contents of file as an option ROM.  This option is useful to load
+things like EtherBoot.
+
+@item -name string
+Sets the name of the guest.  This name will be display in the SDL window
+caption.  The name will also be used for the VNC server.
+
+@end table
+
+Display options:
+@table @option
+
 @item -nographic
 
 Normally, QEMU uses SDL to display the VGA output. With this option,
@@ -257,21 +308,69 @@ available screen space. This makes the u
 available screen space. This makes the using QEMU in a dedicated desktop
 workspace more convenient.
 
-@item -vnc display
+@item -full-screen
+Start in full screen.
+
+@item -vnc display[,option[,option[,...]]]
 
 Normally, QEMU uses SDL to display the VGA output.  With this option,
 you can have QEMU listen on VNC display @var{display} and redirect the VGA
 display over the VNC session.  It is very useful to enable the usb
 tablet device when using this option (option @option{-usbdevice
 tablet}). When using the VNC display, you must use the @option{-k}
-option to set the keyboard layout if you are not using en-us.
-
-@var{display} may be in the form @var{interface:d}, in which case connections
-will only be allowed from @var{interface} on display @var{d}. Optionally,
-@var{interface} can be omitted.  @var{display} can also be in the form
-@var{unix:path} where @var{path} is the location of a unix socket to listen for
-connections on.
-
+option to set the keyboard layout if you are not using en-us. Available
+options for @var{display} are
+
+@table @code
+
+@item @var{interface:d}
+
+TCP connections will only be allowed from @var{interface} on display @var{d}.
+By convention the TCP port is 5900+@var{d}. Optionally, @var{interface} can
+be omitted in which case the server will bind to all interfaces.
+
+@item @var{unix:path}
+
+Connections will be allowed over UNIX domain sockets where @var{path} is the
+location of a unix socket to listen for connections on.
+
+@item @var{none}
+
+VNC is initialized by not started. The monitor @code{change} command can be used
+to later start the VNC server.
+
+@end table
+
+Following the @var{display} value there may be one or more @var{option} flags
+separated by commas. Valid options are
+
+@table @code
+
+@item @var{tls}
+
+Require that client use TLS when communicating with the VNC server. This
+uses anonymous TLS credentials so is susceptible to a man-in-the-middle
+attack. It is recommended that this option be combined with either the
+@var{x509} or @var{x509verify} options.
+
+@item @var{x509}
+
+Valid if @var{tls} is specified. Require that x509 credentials are used
+for negotiating the TLS session. The server will send its x509 certificate
+to the client. It is recommended that a password be set on the VNC server
+to provide authentication of the client when this is used.
+
+@item @var{x509verify}
+
+Valid if @var{tls} is specified. Require that x509 credentials are used
+for negotiating the TLS session. The server will send its x509 certificate
+to the client, and request that the client send its own x509 certificate.
+The server will validate the client's certificate against the CA certificate,
+and reject clients when validation fails. If the certificate authority is
+trusted, this is a sufficient authentication mechanism. You may still wish
+to set a password on the VNC server as a second authentication layer.
+
+@end table
 
 @item -k language
 
@@ -290,53 +389,32 @@ de  en-us  fi  fr-be  hr     it  lv  nl-
 
 The default is @code{en-us}.
 
-@item -audio-help
-
-Will show the audio subsystem help: list of drivers, tunable
-parameters.
-
-@item -soundhw card1,card2,... or -soundhw all
-
-Enable audio and selected sound hardware. Use ? to print all
-available sound hardware.
-
-@example
-qemu -soundhw sb16,adlib hda
-qemu -soundhw es1370 hda
-qemu -soundhw all hda
-qemu -soundhw ?
-@end example
-
-@item -localtime
-Set the real time clock to local time (the default is to UTC
-time). This option is needed to have correct date in MS-DOS or
-Windows.
-
-@item -full-screen
-Start in full screen.
-
-@item -pidfile file
-Store the QEMU process PID in @var{file}. It is useful if you launch QEMU
-from a script.
-
-@item -daemonize
-Daemonize the QEMU process after initialization.  QEMU will not detach from
-standard IO until it is ready to receive connections on any of its devices.
-This option is a useful way for external programs to launch QEMU without having
-to cope with initialization race conditions.
-
-@item -win2k-hack
-Use it when installing Windows 2000 to avoid a disk full bug. After
-Windows 2000 is installed, you no longer need this option (this option
-slows down the IDE transfers).
-
-@item -option-rom file
-Load the contents of file as an option ROM.  This option is useful to load
-things like EtherBoot.
-
-@item -name string
-Sets the name of the guest.  This name will be display in the SDL window
-caption.  The name will also be used for the VNC server.
+@item -x509cacert filename
+
+Specify the path to the CA certificate stored in PEM format. This
+parameter is mandatory if using the @var{x509} or @var{x509} options
+with the VNC server. See the @ref{vnc_security} section for details
+on generating certificates.
+
+@item -x509cacrl filename
+
+Specify the path to the CA certificate revocation list stored in PEM
+format. This parameter is optional. See the @ref{vnc_security} section
+for details on generating certificates.
+
+@item -x509cert filename
+
+Specify the path to the server certificate stored in PEM format..
+This parameter is mandatory if using the @var{x509} or @var{x509}
+options with the VNC server. See the @ref{vnc_security} section
+for details on generating certificates.
+
+@item -x509key filename
+
+Specify the path to the server private key stored in PEM format.
+This parameter is mandatory if using the @var{x509} or @var{x509}
+options with the VNC server. See the @ref{vnc_security} section
+for details on generating certificates.
 
 @end table
 
@@ -862,8 +940,23 @@ Quit the emulator.
 @item eject [-f] device
 Eject a removable medium (use -f to force it).
 
-@item change device filename
-Change a removable medium.
+@item change device setting
+
+Change the configuration of a device
+
+@table @option
+@item change diskdevice filename
+Change the medium for a removable disk device to point to @var{filename}
+
+@item change vnc display,options
+Change the configuration of the VNC server. The valid syntax for @var{display}
+and @var{options} are described at @ref{sec_invocation}. When using this command
+it will prompt for a password to be entered. This is the password that clients
+will use to authenticate with the VNC server. If no passwd is desired, simply
+hit 'enter' without typing any characters. VNC passwords are only significant
+upto 8 letters.
+
+@end table
 
 @item screendump filename
 Save screen into PPM image @var{filename}.
@@ -1421,6 +1514,222 @@ When relaunching QEMU, you may have to u
 When relaunching QEMU, you may have to unplug and plug again the USB
 device to make it work again (this is a bug).
 
+@node vnc_security
+@section VNC security
+
+The VNC server capability provides access to the graphical console
+of the guest VM across the network. This has a number of security
+considerations depending on the deployment scenarios.
+
+@menu
+* vnc_sec_none::
+* vnc_sec_password::
+* vnc_sec_certificate::
+* vnc_sec_certificate_verify::
+* vnc_sec_certificate_pw::
+* vnc_generate_cert::
+@end menu
+@node vnc_sec_none
+@subsection Without passwords
+
+The simplest VNC server setup does not include any form of authentication.
+For this setup it is recommended to restrict it to listen on a UNIX domain
+socket only. For example
+
+@example
+qemu [...OPTIONS...] -vnc unix:/home/joebloggs/.qemu-myvm-vnc
+@end example
+
+This ensures that only users on local box with read/write access to that
+path can access the VNC server. To securely access the VNC server from a
+remote machine, a combination of netcat+ssh can be used to provide a secure
+tunnel.
+
+@node vnc_sec_password
+@subsection With passwords
+
+The VNC protocol has limited support for password based authentication. Since
+the protocol limits passwords to 8 characters it should not be considered
+to provide high security. The password can be fairly easily brute-forced by
+a client making repeat connections. For this reason, a VNC server using password
+authentication should be restricted to only listen on the loopback interface
+or UNIX domain sockets. The password can be set by passing '-vnc none' on the
+command line and then using the QEMU monitor:
+
+@example
+qemu [...OPTIONS...] -vnc none -monitor stdio
+(qemu) change vnc :1
+Password: ********
+(qemu)
+@end example
+
+@node vnc_sec_certificate
+@subsection With x509 certificates
+
+The QEMU VNC server also implements the VeNCrypt extension allowing use of
+TLS for encryption of the session, and x509 certificates for authentication.
+The use of x509 certificates is strongly recommended, because TLS on its
+own is susceptible to man-in-the-middle attacks. Basic x509 certificate
+support provides a secure session, but no authentication. This allows any
+client to connect, and provides an encrypted session.
+
+@example
+qemu [...OPTIONS...] -vnc :1,tls,x509 -monitor stdio \
+    -x509cacert /etc/pki/CA/cacert.pem \
+    -x509cert /etc/pki/qemu/servercert.pem \
+    -x509key /etc/pki/qemu/private/serverkey.pem
+@end example
+
+In the above example @code{/etc/pki} can be replaced with @code{$HOME/.pki}
+if running as a non-root user. NB the @code{serverkey.pem} should be protected
+to only be readable by the user owning it.
+
+@node vnc_sec_certificate_verify
+@subsection With x509 certificates and client verification
+
+Certificates can also provide a means to authenticate the client connecting.
+The server will request that the client provide a certificate, which it will
+then validate against the CA certificate. This is a good choice if deploying
+in an environment with a private internal certificate authority.
+
+@example
+qemu [...OPTIONS...] -vnc tls,x509verify -monitor stdio \
+    -x509cacert /etc/pki/CA/cacert.pem \
+    -x509cert /etc/pki/qemu/servercert.pem \
+    -x509key /etc/pki/qemu/private/serverkey.pem
+@end example
+
+
+@node vnc_sec_certificate_pw
+@subsection With x509 certificates, client verification and passwords
+
+Finally, the previous method can be combined with VNC password authentication
+to provide two layers of authentication for clients.
+
+@example
+qemu [...OPTIONS...] -vnc none -monitor stdio \
+    -x509cacert /etc/pki/CA/cacert.pem \
+    -x509cert /etc/pki/qemu/servercert.pem \
+    -x509key /etc/pki/qemu/private/serverkey.pem
+(qemu) change vnc,tls,x509verify :1
+Password: ********
+(qemu)
+@end example
+
+@node vnc_generate_cert
+@subsection Generating certificates for VNC
+
+The GNU TLS packages provides a command called @code{certtool} which can
+be used to generate certificates and keys in PEM format. At a minimum it
+is neccessary to setup a certificate authority, and issue certificates to
+each server. If using certificates for authentication, then each client
+will also need to be issued a certificate.
+
+@menu
+* vnc_generate_ca::
+* vnc_generate_server::
+* vnc_generate_client::
+@end menu
+@node vnc_generate_ca
+@subsubsection Setup the Certificate Authority
+
+This step only needs to be performed once per organization / organizational
+unit. First the CA needs a private key. This key must be kept VERY secret
+and secure. If this key is compromised the entire trust chain of the certificates
+issued with it is lost.
+
+@example
+# certtool --generate-privkey > cakey.pem
+@end example
+
+A CA needs to have a public certificate. For simplicity it can be a self-signed
+certificate, or one issue by a commercial certificate issuing authority. To
+generate a self-signed certificate requires one core piece of information, the
+name of the organization.
+
+@example
+# cat > ca.info <<EOF
+cn = Name of your organization
+ca
+cert_signing_key
+EOF
+# certtool --generate-self-signed \
+           --load-privkey cakey.pem
+           --template ca.info \
+           --outfile cacert.pem
+@end example
+
+The cacert.pem file should be copied to all servers and clients wishing to utilize
+TLS support in the VNC server. By convention CA certificates are kept in /etc/pki
+or $HOME/.pki
+
+@node vnc_generate_server
+@subsubsection Issuing server certificates
+
+Each server (or host) needs to be issued with a key and certificate. When connecting
+the certificate is sent to the client which validates it against the CA certificate.
+The core piece of information for a server certificate is the hostname. This should
+be the fully qualified hostname that the client will connect with, since the client
+will typically also verify the hostname in the certificate. On the host holding the
+secure CA private key:
+
+@example
+# cat > server.info <<EOF
+organization = Name  of your organization
+cn = server.foo.example.com
+tls_www_server
+encryption_key
+signing_key
+EOF
+# certtool --generate-privkey > serverkey.pem
+# certtool --generate-certificate \
+           --load-ca-certificate cacert.pem \
+           --load-ca-privkey cakey.pem \
+           --load-privkey server serverkey.pem \
+           --template server.info \
+           --outfile servercert.pem
+@end example
+
+The @code{serverkey.pem} and @code{servercert.pem} files should now be securely copied
+to the server for which they were generated. By convention they are kept in
+@code{/etc/pki/qemu/servercert.pem} and @code{/etc/pki/qemu/private/serverkey.pem}, or
+the equivalent per user directory @code{$HOME/.pki}.
+
+@node vnc_generate_client
+@subsubsection Issuing client certificates
+
+If the QEMU VNC server is to use the @code{x509verify} option to validate client
+certificates as its authentication mechanism, each client also needs to be issued
+a certificate. The client certificate contains enough metadata to uniquely identify
+the client, typically organization, state, city, building, etc. On the host holding
+the secure CA private key:
+
+@example
+# cat > client.info <<EOF
+country = GB
+state = London
+locality = London
+organiazation = Name of your organization
+cn = client.foo.example.com
+tls_www_client
+encryption_key
+signing_key
+EOF
+# certtool --generate-privkey > clientkey.pem
+# certtool --generate-certificate \
+           --load-ca-certificate cacert.pem \
+           --load-ca-privkey cakey.pem \
+           --load-privkey server clientkey.pem \
+           --template client.info \
+           --outfile clientcert.pem
+@end example
+
+The @code{clientkey.pem} and @code{clientcert.pem} files should now be securely copied
+to the client for which they were generated. By convention they are kept in
+@code{/etc/pki/qemu/clientcert.pem} and @code{/etc/pki/qemu/private/clientkey.pem}, or
+the equivalent per user directory @code{$HOME/.pki}.
+
+
 @node gdb_usage
 @section GDB usage
 

-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Qemu-devel] PATCH 2/8: Extend monitor 'change' command for VNC
  2007-07-31 19:25 ` [Qemu-devel] PATCH 2/8: Extend monitor 'change' command for VNC Daniel P. Berrange
@ 2007-08-01  1:43   ` Anthony Liguori
  0 siblings, 0 replies; 19+ messages in thread
From: Anthony Liguori @ 2007-08-01  1:43 UTC (permalink / raw)
  To: Daniel P. Berrange, qemu-devel

Daniel P. Berrange wrote:
> This patch extends the QEMU monitor 'change' command so that it can
> be used to change the configuration of the VNC server. On the command
> line the user can use -vnc none, and then issue the 'change vnc :1'
> command later from the monitor. This is utilized in the next patch
> to let the monitor fetch a password for the server. The concept could
> also be extended to serial & parallel devices allowing changing of
> their configuration on the fly.
>   

I've had a very similar patch to this in my queue for a while now.  I 
definitely think this is the right thing to do.

Regards,

Anthony Liguori

> diff -r e85f07144b6c monitor.c
> --- a/monitor.c	Tue Jul 31 10:53:15 2007 -0400
> +++ b/monitor.c	Tue Jul 31 10:55:31 2007 -0400
> @@ -386,7 +386,7 @@ static void do_eject(int force, const ch
>      eject_device(bs, force);
>  }
>  
> -static void do_change(const char *device, const char *filename)
> +static void do_change_block(const char *device, const char *filename)
>  {
>      BlockDriverState *bs;
>  
> @@ -399,6 +399,21 @@ static void do_change(const char *device
>          return;
>      bdrv_open(bs, filename, 0);
>      qemu_key_check(bs, filename);
> +}
> +
> +static void do_change_vnc(const char *target)
> +{
> +    if (vnc_display_open(NULL, target) < 0)
> +	term_printf("could not start VNC server on %s\n", target);
> +}
> +
> +static void do_change(const char *device, const char *target)
> +{
> +    if (strcmp(device, "vnc") == 0) {
> +	do_change_vnc(target);
> +    } else {
> +	do_change_block(device, target);
> +    }
>  }
>  
>  static void do_screen_dump(const char *filename)
> diff -r e85f07144b6c vnc.c
> --- a/vnc.c	Tue Jul 31 10:53:15 2007 -0400
> +++ b/vnc.c	Tue Jul 31 10:55:06 2007 -0400
> @@ -1208,7 +1208,7 @@ void vnc_display_init(DisplayState *ds)
>  
>  void vnc_display_close(DisplayState *ds)
>  {
> -    VncState *vs = (VncState *)ds->opaque;
> +    VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
>  
>      if (vs->display) {
>  	qemu_free(vs->display);
> @@ -1230,7 +1230,7 @@ int vnc_display_open(DisplayState *ds, c
>      int reuse_addr, ret;
>      socklen_t addrlen;
>      const char *p;
> -    VncState *vs = (VncState *)ds->opaque;
> +    VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
>  
>      vnc_display_close(ds);
>      if (strcmp(arg, "none") == 0)
>
>   

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Qemu-devel] PATCH 3/8: VNC password authentication
  2007-07-31 19:26 ` [Qemu-devel] PATCH 3/8: VNC password authentication Daniel P. Berrange
@ 2007-08-01  1:46   ` Anthony Liguori
  2007-08-01 16:26     ` Daniel P. Berrange
  0 siblings, 1 reply; 19+ messages in thread
From: Anthony Liguori @ 2007-08-01  1:46 UTC (permalink / raw)
  To: Daniel P. Berrange, qemu-devel

Daniel P. Berrange wrote:
> This patch introduces support for VNC protocols upto 3.8 and with
> it, support for password based authentication. VNC's password based
> authentication is not entirely secure, but it is a standard and the
> RFB spec requires that all clients support it. The password can be
> provided by using the monitor 'change vnc :1' and it will prompt for
> a password to be entered. Passwords have upto 8 letters of context.
> Pressing 'enter' without entering any characters disables password
> auth in the server. NB, we need a custom copy of d3des here because
> VNC uses a 'special' modification of the algorithm. This d3des code
> is public domain & in all other VNC servers & clients.
>   

I think it may be better to have a command to explicitly set the vnc 
password.  Issuing "change vnc :1" just to change the password is a 
little awkward IMHO.

> -
> -    vnc_write_u32(vs, 1); /* None */
> -    vnc_flush(vs);
> -
> -    vnc_read_when(vs, protocol_client_init, 1);
> +    VNC_DEBUG("Client request protocol version %d.%d\n", vs->major, vs->minor);
> +    if (vs->major != 3 ||
> +	(vs->minor != 3 &&
> +	 vs->minor != 7 &&
> +	 vs->minor != 8)) {
> +	VNC_DEBUG("Unsupported client version\n");
> +	vnc_write_u32(vs, VNC_AUTH_INVALID);
> +	vnc_flush(vs);
> +	vnc_client_error(vs);
> +	return 0;
> +    }
>   

A very popular VNC client uses 3.5 as the protocol version.  I believe 
the specification requires that 3.5 be treated at 3.3 because of that.

Regards,

Anthony Liguori

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Qemu-devel] PATCH 4/8: VeNCrypt basic TLS support
  2007-07-31 19:27 ` [Qemu-devel] PATCH 4/8: VeNCrypt basic TLS support Daniel P. Berrange
@ 2007-08-01  1:50   ` Anthony Liguori
  2007-08-01 16:28     ` Daniel P. Berrange
  0 siblings, 1 reply; 19+ messages in thread
From: Anthony Liguori @ 2007-08-01  1:50 UTC (permalink / raw)
  To: Daniel P. Berrange, qemu-devel

Daniel P. Berrange wrote:
> This patch introduces minimal support for the VeNCrypt protocol
> extension. This layers use of TLS (aka SSL) into the VNC data stream,
> providing session encryption. This patch is the bare minimum protocol
> support. It is enabled by using the 'tls' option flag eg "-vnc :1,tls'
> This is not secure on its own since it uses anonymous credentials.
> The next patches will introduce x509 certificate credentials.
>
> The configure script is setup to that TLS is only compiled in if the
> --enable-vnc-tls flag is provided. This should avoid any breakage on
> platforms without the GNU TLS libraries.
>
> diff -r a1fa771c6cf9 Makefile.target
> --- a/Makefile.target	Tue Jul 31 14:50:01 2007 -0400
> +++ b/Makefile.target	Tue Jul 31 14:50:03 2007 -0400
> @@ -402,6 +402,11 @@ endif
>  endif
>  AUDIODRV+= wavcapture.o
>  
> +ifdef CONFIG_VNC_TLS
> +CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS)
> +LIBS += $(CONFIG_VNC_TLS_LIBS)
> +endif
> +
>  VL_OBJS += i2c.o smbus.o
>  
>  # SCSI layer
> diff -r a1fa771c6cf9 configure
> --- a/configure	Tue Jul 31 14:50:01 2007 -0400
> +++ b/configure	Tue Jul 31 14:50:03 2007 -0400
> @@ -89,6 +89,7 @@ fmod="no"
>  fmod="no"
>  fmod_lib=""
>  fmod_inc=""
> +vnc_tls="no"
>  bsd="no"
>  linux="no"
>  kqemu="no"
> @@ -252,6 +253,8 @@ for opt do
>    ;;
>    --fmod-inc=*) fmod_inc="$optarg"
>    ;;
> +  --enable-vnc-tls) vnc_tls="yes"
> +  ;;
>    --enable-mingw32) mingw32="yes" ; cross_prefix="i386-mingw32-" ; linux_user="no"
>    ;;
>    --disable-slirp) slirp="no"
> @@ -362,6 +365,7 @@ echo "  --enable-alsa            enable 
>  echo "  --enable-alsa            enable ALSA audio driver"
>  echo "  --enable-fmod            enable FMOD audio driver"
>  echo "  --enable-dsound          enable DirectSound audio driver"
> +echo "  --enable-vnc-tls         enable TLS encryption for VNC server"
>  echo "  --enable-system          enable all system emulation targets"
>  echo "  --disable-system         disable all system emulation targets"
>  echo "  --enable-linux-user      enable all linux usermode emulation targets"
> @@ -589,6 +593,16 @@ fi # -z $sdl
>  fi # -z $sdl
>  
>  ##########################################
> +# VNC TLS detection
> +if test "$vnc_tls" = "yes" ; then
> +  `pkg-config gnutls` || vnc_tls="no"
> +fi
> +if test "$vnc_tls" = "yes" ; then
> +  vnc_tls_cflags=`pkg-config --cflags gnutls`
> +  vnc_tls_libs=`pkg-config --libs gnutls`
> +fi
> +
> +##########################################
>  # alsa sound support libraries

Since it's possible to probe for gnutls support, why not just enable it 
by default and disable it if it's not available?

> diff -r a1fa771c6cf9 vl.c
> --- a/vl.c	Tue Jul 31 14:50:01 2007 -0400
> +++ b/vl.c	Tue Jul 31 14:50:03 2007 -0400
> @@ -6458,7 +6458,7 @@ void main_loop_wait(int timeout)
>              if (FD_ISSET(ioh->fd, &rfds)) {
>                  ioh->fd_read(ioh->opaque);
>              }
> -            if (FD_ISSET(ioh->fd, &wfds)) {
> +            if (!ioh->deleted && ioh->fd_write && FD_ISSET(ioh->fd, &wfds)) {
>                  ioh->fd_write(ioh->opaque);
>              }
>          }
>   

I thought this was fixed already.  At any rate, it should be a separate 
patch.

> +#if CONFIG_VNC_TLS
> +ssize_t vnc_tls_push(gnutls_transport_ptr_t transport,
> +		     const void *data,
> +		     size_t len) {
> +    struct VncState *vs = (struct VncState *)transport;
> +    int ret, lastErrno;
>   

s/lastErrno/last_errno/g

> + retry:
> +    ret = send(vs->csock, data, len, 0);
> +    lastErrno = errno;
> +    VNC_DEBUG("Send %d errno %d\n", ret, ret < 0 ? lastErrno : 0);
> +    if (ret < 0) {
> +	if (lastErrno == EINTR)
> +	    goto retry;
> +	errno = lastErrno;
> +	return -1;
> +    }
> +    return ret;
> +}
>   

Regards,

Anthony Liguor

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Qemu-devel] PATCH 7/8: command line args for x509 cert paths
  2007-07-31 19:29 ` [Qemu-devel] PATCH 7/8: command line args for x509 cert paths Daniel P. Berrange
@ 2007-08-01  1:54   ` Anthony Liguori
  2007-08-01 16:31     ` Daniel P. Berrange
  0 siblings, 1 reply; 19+ messages in thread
From: Anthony Liguori @ 2007-08-01  1:54 UTC (permalink / raw)
  To: Daniel P. Berrange, qemu-devel

Daniel P. Berrange wrote:
> This final code patch adds 4 new command line arguments to QEMU to allow the
> certificate files to be specified. The '-x509cacert', '-x509cert' and '-x509key'
> parameters are mandatory if the 'x509' or 'x509verify' flags are used when
> setting up the VNC server. If the certificates are not provided, all client
> authentication attempts will be rejected.
>   

It concerns me a little to add 4 new command line options.  Perhaps just 
supply a directory and hard code the names of each file?  Then it could 
even be specified as -vnc 
[proto]:<proto-arg>[,tls[,x509[:/path/to/x509/certs]]]  with a 
reasonable default provided.

What do you think?

Regards,

Anthony Liguori

> diff -r f38519b13575 vl.c
> --- a/vl.c	Tue Jul 31 14:51:31 2007 -0400
> +++ b/vl.c	Tue Jul 31 14:51:32 2007 -0400
> @@ -6701,6 +6701,12 @@ static void help(int exitcode)
>             "-no-reboot      exit instead of rebooting\n"
>             "-loadvm file    start right away with a saved state (loadvm in monitor)\n"
>  	   "-vnc display    start a VNC server on display\n"
> +#if CONFIG_VNC_TLS
> +	   "-x509cacert FILE  x509 CA certificate for TLS services\n"
> +	   "-x509cacrl FILE   x509 CA certificate revocation list for TLS services\n"
> +	   "-x509cert FILE    x509 public certificate for TLS services\n"
> +	   "-x509key FILE     x509 private key for TLS services\n"
> +#endif
>  #ifndef _WIN32
>  	   "-daemonize      daemonize QEMU after initializing\n"
>  #endif
> @@ -6796,6 +6802,12 @@ enum {
>      QEMU_OPTION_usbdevice,
>      QEMU_OPTION_smp,
>      QEMU_OPTION_vnc,
> +#if CONFIG_VNC_TLS
> +    QEMU_OPTION_x509cacert,
> +    QEMU_OPTION_x509cacrl,
> +    QEMU_OPTION_x509cert,
> +    QEMU_OPTION_x509key,
> +#endif
>      QEMU_OPTION_no_acpi,
>      QEMU_OPTION_no_reboot,
>      QEMU_OPTION_show_cursor,
> @@ -6889,6 +6901,12 @@ const QEMUOption qemu_options[] = {
>      { "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice },
>      { "smp", HAS_ARG, QEMU_OPTION_smp },
>      { "vnc", HAS_ARG, QEMU_OPTION_vnc },
> +#if CONFIG_VNC_TLS
> +    { "x509cacert", HAS_ARG, QEMU_OPTION_x509cacert },
> +    { "x509cacrl", HAS_ARG, QEMU_OPTION_x509cacrl },
> +    { "x509cert", HAS_ARG, QEMU_OPTION_x509cert },
> +    { "x509key", HAS_ARG, QEMU_OPTION_x509key },
> +#endif
>  
>      /* temporary options */
>      { "usb", 0, QEMU_OPTION_usb },
> @@ -7171,6 +7189,9 @@ int main(int argc, char **argv)
>      int fds[2];
>      const char *pid_file = NULL;
>      VLANState *vlan;
> +#if CONFIG_VNC_TLS
> +    const char *x509cacert = NULL, *x509cacrl = NULL, *x509cert = NULL, *x509key = NULL;
> +#endif
>  
>      LIST_INIT (&vm_change_state_head);
>  #ifndef _WIN32
> @@ -7648,6 +7669,20 @@ int main(int argc, char **argv)
>  	    case QEMU_OPTION_vnc:
>  		vnc_display = optarg;
>  		break;
> +#if CONFIG_VNC_TLS
> +	    case QEMU_OPTION_x509cacert:
> +		x509cacert = optarg;
> +		break;
> +	    case QEMU_OPTION_x509cacrl:
> +		x509cacrl = optarg;
> +		break;
> +	    case QEMU_OPTION_x509cert:
> +		x509cert = optarg;
> +		break;
> +	    case QEMU_OPTION_x509key:
> +		x509key = optarg;
> +		break;
> +#endif
>              case QEMU_OPTION_no_acpi:
>                  acpi_enabled = 0;
>                  break;
> @@ -7945,6 +7980,10 @@ int main(int argc, char **argv)
>          dumb_display_init(ds);
>      } else if (vnc_display != NULL) {
>          vnc_display_init(ds);
> +#if CONFIG_VNC_TLS
> +        if (vnc_set_x509_credentials(ds, x509cacert, x509cacrl, x509cert, x509key) < 0)
> +            exit(1);
> +#endif
>          if (vnc_display_open(ds, vnc_display, NULL) < 0)
>              exit(1);
>      } else {
> diff -r f38519b13575 vl.h
> --- a/vl.h	Tue Jul 31 14:51:31 2007 -0400
> +++ b/vl.h	Tue Jul 31 14:51:32 2007 -0400
> @@ -971,6 +971,13 @@ void vnc_display_close(DisplayState *ds)
>  void vnc_display_close(DisplayState *ds);
>  int vnc_display_open(DisplayState *ds, const char *display, const char *password);
>  void do_info_vnc(void);
> +#if CONFIG_VNC_TLS
> +int vnc_set_x509_credentials(DisplayState *ds,
> +			     const char *cacert,
> +			     const char *cacrl,
> +			     const char *cert,
> +			     const char *key);
> +#endif
>  
>  /* x_keymap.c */
>  extern uint8_t _translate_keycode(const int key);
> diff -r f38519b13575 vnc.c
> --- a/vnc.c	Tue Jul 31 14:51:31 2007 -0400
> +++ b/vnc.c	Tue Jul 31 14:51:32 2007 -0400
> @@ -142,6 +142,11 @@ struct VncState
>  #if CONFIG_VNC_TLS
>      int subauth;
>      int x509verify;
> +
> +    char *x509cacert;
> +    char *x509cacrl;
> +    char *x509cert;
> +    char *x509key;
>  #endif
>      char challenge[VNC_AUTH_CHALLENGE_SIZE];
>  
> @@ -1378,36 +1383,50 @@ static gnutls_anon_server_credentials vn
>  }
>  
>  
> -static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(void)
> +static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncState *vs)
>  {
>      gnutls_certificate_credentials_t x509_cred;
>      int ret;
> -    struct stat st;
> +
> +    if (!vs->x509cacert) {
> +	VNC_DEBUG("No CA x509 certificate specified\n");
> +	return NULL;
> +    }
> +    if (!vs->x509cert) {
> +	VNC_DEBUG("No server x509 certificate specified\n");
> +	return NULL;
> +    }
> +    if (!vs->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, CA_FILE, GNUTLS_X509_FMT_PEM)) < 0) {
> +    if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
> +						      vs->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, CERT_FILE, KEY_FILE,
> +    if ((ret = gnutls_certificate_set_x509_key_file (x509_cred,
> +						     vs->x509cert,
> +						     vs->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 (stat(CRL_FILE, &st) < 0) {
> -	if (errno != ENOENT) {
> -	    gnutls_certificate_free_credentials(x509_cred);
> -	    return NULL;
> -	}
> -    } else {
> -	if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred, CRL_FILE, GNUTLS_X509_FMT_PEM)) < 0) {
> +    if (vs->x509cacrl) {
> +	if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
> +							vs->x509cacrl,
> +							GNUTLS_X509_FMT_PEM)) < 0) {
>  	    VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret));
>  	    gnutls_certificate_free_credentials(x509_cred);
>  	    return NULL;
> @@ -1623,7 +1642,7 @@ static int vnc_start_tls(struct VncState
>  	}
>  
>  	if (NEED_X509_AUTH(vs)) {
> -	    gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred();
> +	    gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred(vs);
>  	    if (!x509_cred) {
>  		gnutls_deinit(vs->tls_session);
>  		vs->tls_session = NULL;
> @@ -1888,6 +1907,43 @@ void vnc_display_init(DisplayState *ds)
>      vnc_dpy_resize(vs->ds, 640, 400);
>  }
>  
> +#if CONFIG_VNC_TLS
> +int vnc_set_x509_credentials(DisplayState *ds,
> +			     const char *cacert,
> +			     const char *cacrl,
> +			     const char *cert,
> +			     const char *key)
> +{
> +    VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
> +
> +    if (vs->x509cacert) {
> +	free(vs->x509cacert);
> +	vs->x509cacert = NULL;
> +    }
> +    if (vs->x509cacrl) {
> +	free(vs->x509cacrl);
> +	vs->x509cacrl = NULL;
> +    }
> +    if (vs->x509cert) {
> +	free(vs->x509cert);
> +	vs->x509cert = NULL;
> +    }
> +    if (vs->x509key) {
> +	free(vs->x509key);
> +	vs->x509key = NULL;
> +    }
> +    if (cacert && !(vs->x509cacert = qemu_strdup(cacert)))
> +	return -1;
> +    if (cacrl && !(vs->x509cacrl = qemu_strdup(cacrl)))
> +	return -1;
> +    if (cert && !(vs->x509cert = qemu_strdup(cert)))
> +	return -1;
> +    if (key && !(vs->x509key = qemu_strdup(key)))
> +	return -1;
> +    return 0;
> +}
> +#endif /* CONFIG_VNC_TLS */
> +
>  void vnc_display_close(DisplayState *ds)
>  {
>      VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
>
>   

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Qemu-devel] PATCH 0/8: Authentication support for the VNC server
  2007-07-31 19:23 [Qemu-devel] PATCH 0/8: Authentication support for the VNC server Daniel P. Berrange
                   ` (7 preceding siblings ...)
  2007-07-31 19:30 ` [Qemu-devel] PATCH 8/8: document all VNC authentication options Daniel P. Berrange
@ 2007-08-01  1:55 ` Anthony Liguori
  8 siblings, 0 replies; 19+ messages in thread
From: Anthony Liguori @ 2007-08-01  1:55 UTC (permalink / raw)
  To: Daniel P. Berrange, qemu-devel

Authentication support is a commonly requested feature.  I think this is 
definitely the right approach to take.

Regards,

Anthony Liguori

Daniel P. Berrange wrote:
> The current VNC server implementation does not have support for the
> authentication of incoming client connections. The following series
> of patches provide support for a number of alternatives, all compliant
> with the VNC protocol spec. The simplest mechanism (and the weakest)
> is the traditional VNC password scheme based on weak d3des hashing of
> an 8 byte key. The more serious mechanism uses TLS for data encryption
> of the entire session, and x509 certificates for both client and server
> authentication.
>
> The patches are an iteration on the previous work I posted a couple of
> months ago[1]. They are now functionally complete, better tested,
> split up into a patch series for easier review, and their use fully
> documented. Since TLS can be quite perplexing, I also included some
> documentation on how to setup a CA, and issue client & server certs
> in a manner suitable for use with the VNC server.
>
> For the basic VNC password auth, this patch should be compatible with
> any standard VNC client such as RealVNC. The TLS based auth schemes
> require a client that implements the VeNCrypt extension[2]. The client
> from the VeNCrypt[3] project of course is one example. The GTK-VNC[4]
> widget which is used by Virt Manager[5] and Vinagre [6] also support
> it, and are my primary testing platform.
>
> The 8 individual patches will follow shortly in replies to this mail.
>
> Regards,
> Dan.
>
> [1] http://www.mail-archive.com/qemu-devel@nongnu.org/msg08616.html
> [2] http://www.mail-archive.com/qemu-devel@nongnu.org/msg08681.html
> [3] http://sourceforge.net/projects/vencrypt/
> [4] http://gtk-vnc.sourceforge.net/
> [5] http://virt-manager.org/
> [6] http://www.gnome.org/~jwendell/vinagre/
>   

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Qemu-devel] PATCH 3/8: VNC password authentication
  2007-08-01  1:46   ` Anthony Liguori
@ 2007-08-01 16:26     ` Daniel P. Berrange
  2007-08-02 14:35       ` Anthony Liguori
  0 siblings, 1 reply; 19+ messages in thread
From: Daniel P. Berrange @ 2007-08-01 16:26 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel

On Tue, Jul 31, 2007 at 08:46:49PM -0500, Anthony Liguori wrote:
> Daniel P. Berrange wrote:
> >This patch introduces support for VNC protocols upto 3.8 and with
> >it, support for password based authentication. VNC's password based
> >authentication is not entirely secure, but it is a standard and the
> >RFB spec requires that all clients support it. The password can be
> >provided by using the monitor 'change vnc :1' and it will prompt for
> >a password to be entered. Passwords have upto 8 letters of context.
> >Pressing 'enter' without entering any characters disables password
> >auth in the server. NB, we need a custom copy of d3des here because
> >VNC uses a 'special' modification of the algorithm. This d3des code
> >is public domain & in all other VNC servers & clients.
> >  
> 
> I think it may be better to have a command to explicitly set the vnc 
> password.  Issuing "change vnc :1" just to change the password is a 
> little awkward IMHO.

Ok I'll add a separate command for that - any preference for naming.
I thought about 'change vncpassword', but the 'change' command requires
2 args and we'd only have 1 here.  Or if we think there may be other
devices/drivers which will have passwords in the future we could have
'change password vnc' as the command. 

> >-
> >-    vnc_write_u32(vs, 1); /* None */
> >-    vnc_flush(vs);
> >-
> >-    vnc_read_when(vs, protocol_client_init, 1);
> >+    VNC_DEBUG("Client request protocol version %d.%d\n", vs->major, 
> >vs->minor);
> >+    if (vs->major != 3 ||
> >+	(vs->minor != 3 &&
> >+	 vs->minor != 7 &&
> >+	 vs->minor != 8)) {
> >+	VNC_DEBUG("Unsupported client version\n");
> >+	vnc_write_u32(vs, VNC_AUTH_INVALID);
> >+	vnc_flush(vs);
> >+	vnc_client_error(vs);
> >+	return 0;
> >+    }
> >  
> 
> A very popular VNC client uses 3.5 as the protocol version.  I believe 
> the specification requires that 3.5 be treated at 3.3 because of that.

Good point. I'll add support for that.

Regards,
Dan.
-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Qemu-devel] PATCH 4/8: VeNCrypt basic TLS support
  2007-08-01  1:50   ` Anthony Liguori
@ 2007-08-01 16:28     ` Daniel P. Berrange
  0 siblings, 0 replies; 19+ messages in thread
From: Daniel P. Berrange @ 2007-08-01 16:28 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel

On Tue, Jul 31, 2007 at 08:50:29PM -0500, Anthony Liguori wrote:
> Daniel P. Berrange wrote:
> >@@ -362,6 +365,7 @@ echo "  --enable-alsa            enable 
> > echo "  --enable-alsa            enable ALSA audio driver"
> > echo "  --enable-fmod            enable FMOD audio driver"
> > echo "  --enable-dsound          enable DirectSound audio driver"
> >+echo "  --enable-vnc-tls         enable TLS encryption for VNC server"
> > echo "  --enable-system          enable all system emulation targets"
> > echo "  --disable-system         disable all system emulation targets"
> > echo "  --enable-linux-user      enable all linux usermode emulation 
> > targets"
> >@@ -589,6 +593,16 @@ fi # -z $sdl
> > fi # -z $sdl
> > 
> > ##########################################
> >+# VNC TLS detection
> >+if test "$vnc_tls" = "yes" ; then
> >+  `pkg-config gnutls` || vnc_tls="no"
> >+fi
> >+if test "$vnc_tls" = "yes" ; then
> >+  vnc_tls_cflags=`pkg-config --cflags gnutls`
> >+  vnc_tls_libs=`pkg-config --libs gnutls`
> >+fi
> >+
> >+##########################################
> > # alsa sound support libraries
> 
> Since it's possible to probe for gnutls support, why not just enable it 
> by default and disable it if it's not available?

Sure I can make that change - I wasn't sure what people's preference for
this was so I took conservative approach of not enabling it unless it
is explicitly asked for. Happy to change it to enable by default if the
pkg-config probing succeeds, and allow a configure arg to explicitly
disable it.

> >diff -r a1fa771c6cf9 vl.c
> >--- a/vl.c	Tue Jul 31 14:50:01 2007 -0400
> >+++ b/vl.c	Tue Jul 31 14:50:03 2007 -0400
> >@@ -6458,7 +6458,7 @@ void main_loop_wait(int timeout)
> >             if (FD_ISSET(ioh->fd, &rfds)) {
> >                 ioh->fd_read(ioh->opaque);
> >             }
> >-            if (FD_ISSET(ioh->fd, &wfds)) {
> >+            if (!ioh->deleted && ioh->fd_write && FD_ISSET(ioh->fd, 
> >&wfds)) {
> >                 ioh->fd_write(ioh->opaque);
> >             }
> >         }
> >  
> 
> I thought this was fixed already.  At any rate, it should be a separate 
> patch.

Sorry, this chunk wasn't supposed to be included - I'll submit it as a
separate patch.

> >+#if CONFIG_VNC_TLS
> >+ssize_t vnc_tls_push(gnutls_transport_ptr_t transport,
> >+		     const void *data,
> >+		     size_t len) {
> >+    struct VncState *vs = (struct VncState *)transport;
> >+    int ret, lastErrno;
> >  
> 
> s/lastErrno/last_errno/g

Ok.


Regards,
Dan.
-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Qemu-devel] PATCH 7/8: command line args for x509 cert paths
  2007-08-01  1:54   ` Anthony Liguori
@ 2007-08-01 16:31     ` Daniel P. Berrange
  0 siblings, 0 replies; 19+ messages in thread
From: Daniel P. Berrange @ 2007-08-01 16:31 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel

On Tue, Jul 31, 2007 at 08:54:09PM -0500, Anthony Liguori wrote:
> Daniel P. Berrange wrote:
> >This final code patch adds 4 new command line arguments to QEMU to allow 
> >the
> >certificate files to be specified. The '-x509cacert', '-x509cert' and 
> >'-x509key'
> >parameters are mandatory if the 'x509' or 'x509verify' flags are used when
> >setting up the VNC server. If the certificates are not provided, all client
> >authentication attempts will be rejected.
> >  
> 
> It concerns me a little to add 4 new command line options.  Perhaps just 
> supply a directory and hard code the names of each file?  Then it could 
> even be specified as -vnc 
> [proto]:<proto-arg>[,tls[,x509[:/path/to/x509/certs]]]  with a 
> reasonable default provided.

Including it as part of the main vnc arg would be nice as it'd let the admin
set/change it from the monitor too. Merely specifying a directory would be
fine with me - its trivial to symlink files if the admin wants to store them
in some other way.

Dan.
-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Qemu-devel] PATCH 3/8: VNC password authentication
  2007-08-01 16:26     ` Daniel P. Berrange
@ 2007-08-02 14:35       ` Anthony Liguori
  0 siblings, 0 replies; 19+ messages in thread
From: Anthony Liguori @ 2007-08-02 14:35 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: qemu-devel

Daniel P. Berrange wrote:
> On Tue, Jul 31, 2007 at 08:46:49PM -0500, Anthony Liguori wrote:
>   
>> Daniel P. Berrange wrote:
>>     
>>> This patch introduces support for VNC protocols upto 3.8 and with
>>> it, support for password based authentication. VNC's password based
>>> authentication is not entirely secure, but it is a standard and the
>>> RFB spec requires that all clients support it. The password can be
>>> provided by using the monitor 'change vnc :1' and it will prompt for
>>> a password to be entered. Passwords have upto 8 letters of context.
>>> Pressing 'enter' without entering any characters disables password
>>> auth in the server. NB, we need a custom copy of d3des here because
>>> VNC uses a 'special' modification of the algorithm. This d3des code
>>> is public domain & in all other VNC servers & clients.
>>>  
>>>       
>> I think it may be better to have a command to explicitly set the vnc 
>> password.  Issuing "change vnc :1" just to change the password is a 
>> little awkward IMHO.
>>     
>
> Ok I'll add a separate command for that - any preference for naming.
> I thought about 'change vncpassword', but the 'change' command requires
> 2 args and we'd only have 1 here.  Or if we think there may be other
> devices/drivers which will have passwords in the future we could have
> 'change password vnc' as the command. 
>   

I don't really have that strong of preference.

Regards,

Anthony Liguori

>>> -
>>> -    vnc_write_u32(vs, 1); /* None */
>>> -    vnc_flush(vs);
>>> -
>>> -    vnc_read_when(vs, protocol_client_init, 1);
>>> +    VNC_DEBUG("Client request protocol version %d.%d\n", vs->major, 
>>> vs->minor);
>>> +    if (vs->major != 3 ||
>>> +	(vs->minor != 3 &&
>>> +	 vs->minor != 7 &&
>>> +	 vs->minor != 8)) {
>>> +	VNC_DEBUG("Unsupported client version\n");
>>> +	vnc_write_u32(vs, VNC_AUTH_INVALID);
>>> +	vnc_flush(vs);
>>> +	vnc_client_error(vs);
>>> +	return 0;
>>> +    }
>>>  
>>>       
>> A very popular VNC client uses 3.5 as the protocol version.  I believe 
>> the specification requires that 3.5 be treated at 3.3 because of that.
>>     
>
> Good point. I'll add support for that.
>
> Regards,
> Dan.
>   

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [Qemu-devel] PATCH 3/8: VNC password authentication
  2007-08-13 19:25 Daniel P. Berrange
@ 2007-08-13 19:44 ` Daniel P. Berrange
  0 siblings, 0 replies; 19+ messages in thread
From: Daniel P. Berrange @ 2007-08-13 19:44 UTC (permalink / raw)
  To: qemu-devel

This patch introduces support for VNC protocols upto 3.8 and with
it, support for password based authentication. VNC's password based
authentication is not entirely secure, but it is a standard and the
RFB spec requires that all clients support it. The password can be
provided by using the monitor 'change vnc password' and it will prompt
for a password to be entered. Passwords have upto 8 letters of context.
Until the 'change vnc password' monitor command is run, all client
connection attempts will be rejected. This avoids a startup race where
no password would be present. NB, we need a custom copy of d3des here 
because VNC uses a 'special' modification of the algorithm. This d3des
code is public domain & in all other VNC servers & clients. For client
compatability, protocol 3.5 is treated as identical to protocol 3.3.

Example usage:

  qemu [...OPTIONS...] -vnc :1,password -monitor stdio
  (qemu) change vnc password
  Password: ********
  (qemu)


    Signed-off-by: Daniel P. Berrange <berrange@redhat.com>


diff -r 08374728639d Makefile.target
--- a/Makefile.target	Wed Aug 08 15:04:44 2007 -0400
+++ b/Makefile.target	Mon Aug 13 11:25:43 2007 -0400
@@ -482,7 +482,7 @@ ifdef CONFIG_SDL
 ifdef CONFIG_SDL
 VL_OBJS+=sdl.o x_keymap.o
 endif
-VL_OBJS+=vnc.o
+VL_OBJS+=vnc.o d3des.o
 ifdef CONFIG_COCOA
 VL_OBJS+=cocoa.o
 COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit
@@ -543,7 +543,7 @@ sdl.o: sdl.c keymaps.c sdl_keysym.h
 sdl.o: sdl.c keymaps.c sdl_keysym.h
 	$(CC) $(CFLAGS) $(CPPFLAGS) $(SDL_CFLAGS) $(BASE_CFLAGS) -c -o $@ $<
 
-vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h
+vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h d3des.c d3des.h
 	$(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
 
 sdlaudio.o: sdlaudio.c
diff -r 08374728639d d3des.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/d3des.c	Wed Aug 08 15:04:47 2007 -0400
@@ -0,0 +1,434 @@
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC.  Also the bytebit[] array
+ * has been reversed so that the most significant bit in each byte of the
+ * key is ignored, not the least significant.
+ *
+ * These changes are:
+ *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* D3DES (V5.09) -
+ *
+ * A portable, public domain, version of the Data Encryption Standard.
+ *
+ * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
+ * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
+ * code;  Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
+ * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
+ * for humouring me on.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
+ * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
+ */
+
+#include "d3des.h"
+
+static void scrunch(unsigned char *, unsigned long *);
+static void unscrun(unsigned long *, unsigned char *);
+static void desfunc(unsigned long *, unsigned long *);
+static void cookey(unsigned long *);
+
+static unsigned long KnL[32] = { 0L };
+
+static unsigned short bytebit[8]	= {
+	01, 02, 04, 010, 020, 040, 0100, 0200 };
+
+static unsigned long bigbyte[24] = {
+	0x800000L,	0x400000L,	0x200000L,	0x100000L,
+	0x80000L,	0x40000L,	0x20000L,	0x10000L,
+	0x8000L,	0x4000L,	0x2000L,	0x1000L,
+	0x800L, 	0x400L, 	0x200L, 	0x100L,
+	0x80L,		0x40L,		0x20L,		0x10L,
+	0x8L,		0x4L,		0x2L,		0x1L	};
+
+/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */
+
+static unsigned char pc1[56] = {
+	56, 48, 40, 32, 24, 16,  8,	 0, 57, 49, 41, 33, 25, 17,
+	 9,  1, 58, 50, 42, 34, 26,	18, 10,  2, 59, 51, 43, 35,
+	62, 54, 46, 38, 30, 22, 14,	 6, 61, 53, 45, 37, 29, 21,
+	13,  5, 60, 52, 44, 36, 28,	20, 12,  4, 27, 19, 11,  3 };
+
+static unsigned char totrot[16] = {
+	1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 };
+
+static unsigned char pc2[48] = {
+	13, 16, 10, 23,  0,  4,  2, 27, 14,  5, 20,  9,
+	22, 18, 11,  3, 25,  7, 15,  6, 26, 19, 12,  1,
+	40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
+	43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 };
+
+void deskey(key, edf)	/* Thanks to James Gillogly & Phil Karn! */
+unsigned char *key;
+int edf;
+{
+	register int i, j, l, m, n;
+	unsigned char pc1m[56], pcr[56];
+	unsigned long kn[32];
+
+	for ( j = 0; j < 56; j++ ) {
+		l = pc1[j];
+		m = l & 07;
+		pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0;
+		}
+	for( i = 0; i < 16; i++ ) {
+		if( edf == DE1 ) m = (15 - i) << 1;
+		else m = i << 1;
+		n = m + 1;
+		kn[m] = kn[n] = 0L;
+		for( j = 0; j < 28; j++ ) {
+			l = j + totrot[i];
+			if( l < 28 ) pcr[j] = pc1m[l];
+			else pcr[j] = pc1m[l - 28];
+			}
+		for( j = 28; j < 56; j++ ) {
+		    l = j + totrot[i];
+		    if( l < 56 ) pcr[j] = pc1m[l];
+		    else pcr[j] = pc1m[l - 28];
+		    }
+		for( j = 0; j < 24; j++ ) {
+			if( pcr[pc2[j]] ) kn[m] |= bigbyte[j];
+			if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j];
+			}
+		}
+	cookey(kn);
+	return;
+	}
+
+static void cookey(raw1)
+register unsigned long *raw1;
+{
+	register unsigned long *cook, *raw0;
+	unsigned long dough[32];
+	register int i;
+
+	cook = dough;
+	for( i = 0; i < 16; i++, raw1++ ) {
+		raw0 = raw1++;
+		*cook	 = (*raw0 & 0x00fc0000L) << 6;
+		*cook	|= (*raw0 & 0x00000fc0L) << 10;
+		*cook	|= (*raw1 & 0x00fc0000L) >> 10;
+		*cook++ |= (*raw1 & 0x00000fc0L) >> 6;
+		*cook	 = (*raw0 & 0x0003f000L) << 12;
+		*cook	|= (*raw0 & 0x0000003fL) << 16;
+		*cook	|= (*raw1 & 0x0003f000L) >> 4;
+		*cook++ |= (*raw1 & 0x0000003fL);
+		}
+	usekey(dough);
+	return;
+	}
+
+void cpkey(into)
+register unsigned long *into;
+{
+	register unsigned long *from, *endp;
+
+	from = KnL, endp = &KnL[32];
+	while( from < endp ) *into++ = *from++;
+	return;
+	}
+
+void usekey(from)
+register unsigned long *from;
+{
+	register unsigned long *to, *endp;
+
+	to = KnL, endp = &KnL[32];
+	while( to < endp ) *to++ = *from++;
+	return;
+	}
+
+void des(inblock, outblock)
+unsigned char *inblock, *outblock;
+{
+	unsigned long work[2];
+
+	scrunch(inblock, work);
+	desfunc(work, KnL);
+	unscrun(work, outblock);
+	return;
+	}
+
+static void scrunch(outof, into)
+register unsigned char *outof;
+register unsigned long *into;
+{
+	*into	 = (*outof++ & 0xffL) << 24;
+	*into	|= (*outof++ & 0xffL) << 16;
+	*into	|= (*outof++ & 0xffL) << 8;
+	*into++ |= (*outof++ & 0xffL);
+	*into	 = (*outof++ & 0xffL) << 24;
+	*into	|= (*outof++ & 0xffL) << 16;
+	*into	|= (*outof++ & 0xffL) << 8;
+	*into	|= (*outof   & 0xffL);
+	return;
+	}
+
+static void unscrun(outof, into)
+register unsigned long *outof;
+register unsigned char *into;
+{
+	*into++ = (unsigned char)((*outof >> 24) & 0xffL);
+	*into++ = (unsigned char)((*outof >> 16) & 0xffL);
+	*into++ = (unsigned char)((*outof >>  8) & 0xffL);
+	*into++ = (unsigned char)(*outof++	 & 0xffL);
+	*into++ = (unsigned char)((*outof >> 24) & 0xffL);
+	*into++ = (unsigned char)((*outof >> 16) & 0xffL);
+	*into++ = (unsigned char)((*outof >>  8) & 0xffL);
+	*into	=  (unsigned char)(*outof	 & 0xffL);
+	return;
+	}
+
+static unsigned long SP1[64] = {
+	0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L,
+	0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L,
+	0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L,
+	0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L,
+	0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L,
+	0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L,
+	0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L,
+	0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L,
+	0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L,
+	0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L,
+	0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L,
+	0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L,
+	0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L,
+	0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L,
+	0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L,
+	0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L };
+
+static unsigned long SP2[64] = {
+	0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L,
+	0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L,
+	0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L,
+	0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L,
+	0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L,
+	0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L,
+	0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L,
+	0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L,
+	0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L,
+	0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L,
+	0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L,
+	0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L,
+	0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L,
+	0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L,
+	0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L,
+	0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L };
+
+static unsigned long SP3[64] = {
+	0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L,
+	0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L,
+	0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L,
+	0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L,
+	0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L,
+	0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L,
+	0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L,
+	0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L,
+	0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L,
+	0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L,
+	0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L,
+	0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L,
+	0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L,
+	0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L,
+	0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L,
+	0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L };
+
+static unsigned long SP4[64] = {
+	0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+	0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L,
+	0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L,
+	0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L,
+	0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L,
+	0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L,
+	0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L,
+	0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L,
+	0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L,
+	0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L,
+	0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L,
+	0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+	0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L,
+	0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L,
+	0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L,
+	0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L };
+
+static unsigned long SP5[64] = {
+	0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L,
+	0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L,
+	0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L,
+	0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L,
+	0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L,
+	0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L,
+	0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L,
+	0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L,
+	0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L,
+	0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L,
+	0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L,
+	0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L,
+	0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L,
+	0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L,
+	0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L,
+	0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L };
+
+static unsigned long SP6[64] = {
+	0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L,
+	0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L,
+	0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L,
+	0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L,
+	0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L,
+	0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L,
+	0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L,
+	0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L,
+	0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L,
+	0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L,
+	0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L,
+	0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L,
+	0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L,
+	0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L,
+	0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L,
+	0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L };
+
+static unsigned long SP7[64] = {
+	0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L,
+	0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L,
+	0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L,
+	0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L,
+	0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L,
+	0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L,
+	0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L,
+	0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L,
+	0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L,
+	0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L,
+	0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L,
+	0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L,
+	0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L,
+	0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L,
+	0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L,
+	0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L };
+
+static unsigned long SP8[64] = {
+	0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L,
+	0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L,
+	0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L,
+	0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L,
+	0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L,
+	0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L,
+	0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L,
+	0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L,
+	0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L,
+	0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L,
+	0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L,
+	0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L,
+	0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L,
+	0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L,
+	0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L,
+	0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L };
+
+static void desfunc(block, keys)
+register unsigned long *block, *keys;
+{
+	register unsigned long fval, work, right, leftt;
+	register int round;
+
+	leftt = block[0];
+	right = block[1];
+	work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL;
+	right ^= work;
+	leftt ^= (work << 4);
+	work = ((leftt >> 16) ^ right) & 0x0000ffffL;
+	right ^= work;
+	leftt ^= (work << 16);
+	work = ((right >> 2) ^ leftt) & 0x33333333L;
+	leftt ^= work;
+	right ^= (work << 2);
+	work = ((right >> 8) ^ leftt) & 0x00ff00ffL;
+	leftt ^= work;
+	right ^= (work << 8);
+	right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL;
+	work = (leftt ^ right) & 0xaaaaaaaaL;
+	leftt ^= work;
+	right ^= work;
+	leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL;
+
+	for( round = 0; round < 8; round++ ) {
+		work  = (right << 28) | (right >> 4);
+		work ^= *keys++;
+		fval  = SP7[ work		 & 0x3fL];
+		fval |= SP5[(work >>  8) & 0x3fL];
+		fval |= SP3[(work >> 16) & 0x3fL];
+		fval |= SP1[(work >> 24) & 0x3fL];
+		work  = right ^ *keys++;
+		fval |= SP8[ work		 & 0x3fL];
+		fval |= SP6[(work >>  8) & 0x3fL];
+		fval |= SP4[(work >> 16) & 0x3fL];
+		fval |= SP2[(work >> 24) & 0x3fL];
+		leftt ^= fval;
+		work  = (leftt << 28) | (leftt >> 4);
+		work ^= *keys++;
+		fval  = SP7[ work		 & 0x3fL];
+		fval |= SP5[(work >>  8) & 0x3fL];
+		fval |= SP3[(work >> 16) & 0x3fL];
+		fval |= SP1[(work >> 24) & 0x3fL];
+		work  = leftt ^ *keys++;
+		fval |= SP8[ work		 & 0x3fL];
+		fval |= SP6[(work >>  8) & 0x3fL];
+		fval |= SP4[(work >> 16) & 0x3fL];
+		fval |= SP2[(work >> 24) & 0x3fL];
+		right ^= fval;
+		}
+
+	right = (right << 31) | (right >> 1);
+	work = (leftt ^ right) & 0xaaaaaaaaL;
+	leftt ^= work;
+	right ^= work;
+	leftt = (leftt << 31) | (leftt >> 1);
+	work = ((leftt >> 8) ^ right) & 0x00ff00ffL;
+	right ^= work;
+	leftt ^= (work << 8);
+	work = ((leftt >> 2) ^ right) & 0x33333333L;
+	right ^= work;
+	leftt ^= (work << 2);
+	work = ((right >> 16) ^ leftt) & 0x0000ffffL;
+	leftt ^= work;
+	right ^= (work << 16);
+	work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL;
+	leftt ^= work;
+	right ^= (work << 4);
+	*block++ = right;
+	*block = leftt;
+	return;
+	}
+
+/* Validation sets:
+ *
+ * Single-length key, single-length plaintext -
+ * Key	  : 0123 4567 89ab cdef
+ * Plain  : 0123 4567 89ab cde7
+ * Cipher : c957 4425 6a5e d31d
+ *
+ * Double-length key, single-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain  : 0123 4567 89ab cde7
+ * Cipher : 7f1d 0a77 826b 8aff
+ *
+ * Double-length key, double-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain  : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7
+ *
+ * Triple-length key, single-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain  : 0123 4567 89ab cde7
+ * Cipher : de0b 7c06 ae5e 0ed5
+ *
+ * Triple-length key, double-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain  : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5
+ *
+ * d3des V5.0a rwo 9208.07 18:44 Graven Imagery
+ **********************************************************************/
diff -r 08374728639d d3des.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/d3des.h	Wed Aug 08 15:04:47 2007 -0400
@@ -0,0 +1,51 @@
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC.
+ *
+ * These changes are:
+ *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* d3des.h -
+ *
+ *	Headers and defines for d3des.c
+ *	Graven Imagery, 1992.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge
+ *	(GEnie : OUTER; CIS : [71755,204])
+ */
+
+#define EN0	0	/* MODE == encrypt */
+#define DE1	1	/* MODE == decrypt */
+
+extern void deskey(unsigned char *, int);
+/*		      hexkey[8]     MODE
+ * Sets the internal key register according to the hexadecimal
+ * key contained in the 8 bytes of hexkey, according to the DES,
+ * for encryption or decryption according to MODE.
+ */
+
+extern void usekey(unsigned long *);
+/*		    cookedkey[32]
+ * Loads the internal key register with the data in cookedkey.
+ */
+
+extern void cpkey(unsigned long *);
+/*		   cookedkey[32]
+ * Copies the contents of the internal key register into the storage
+ * located at &cookedkey[0].
+ */
+
+extern void des(unsigned char *, unsigned char *);
+/*		    from[8]	      to[8]
+ * Encrypts/Decrypts (according to the key currently loaded in the
+ * internal key register) one block of eight bytes at address 'from'
+ * into the block at address 'to'.  They can be the same.
+ */
+
+/* d3des.h V5.09 rwo 9208.04 15:06 Graven Imagery
+ ********************************************************************/
diff -r 08374728639d monitor.c
--- a/monitor.c	Wed Aug 08 15:04:44 2007 -0400
+++ b/monitor.c	Mon Aug 13 11:36:07 2007 -0400
@@ -403,8 +403,17 @@ static void do_change_block(const char *
 
 static void do_change_vnc(const char *target)
 {
-    if (vnc_display_open(NULL, target) < 0)
-	term_printf("could not start VNC server on %s\n", target);
+    if (strcmp(target, "passwd") == 0 ||
+	strcmp(target, "password") == 0) {
+	char password[9];
+	monitor_readline("Password: ", 1, password, sizeof(password)-1);
+	password[sizeof(password)-1] = '\0';
+	if (vnc_display_password(NULL, password) < 0)
+	    term_printf("could not set VNC server password\n");
+    } else {
+	if (vnc_display_open(NULL, target) < 0)
+	    term_printf("could not start VNC server on %s\n", target);
+    }
 }
 
 static void do_change(const char *device, const char *target)
diff -r 08374728639d vl.h
--- a/vl.h	Wed Aug 08 15:04:44 2007 -0400
+++ b/vl.h	Wed Aug 08 15:04:47 2007 -0400
@@ -971,6 +971,7 @@ void vnc_display_init(DisplayState *ds);
 void vnc_display_init(DisplayState *ds);
 void vnc_display_close(DisplayState *ds);
 int vnc_display_open(DisplayState *ds, const char *display);
+int vnc_display_password(DisplayState *ds, const char *password);
 void do_info_vnc(void);
 
 /* x_keymap.c */
diff -r 08374728639d vnc.c
--- a/vnc.c	Wed Aug 08 15:04:44 2007 -0400
+++ b/vnc.c	Mon Aug 13 11:51:38 2007 -0400
@@ -30,6 +30,15 @@
 
 #include "vnc_keysym.h"
 #include "keymaps.c"
+#include "d3des.h"
+
+// #define _VNC_DEBUG
+
+#ifdef _VNC_DEBUG
+#define VNC_DEBUG(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define VNC_DEBUG(fmt, ...) do { } while (0)
+#endif
 
 typedef struct Buffer
 {
@@ -53,6 +62,20 @@ typedef void VncSendHextileTile(VncState
 #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
+
+enum {
+    VNC_AUTH_INVALID = 0,
+    VNC_AUTH_NONE = 1,
+    VNC_AUTH_VNC = 2,
+    VNC_AUTH_RA2 = 5,
+    VNC_AUTH_RA2NE = 6,
+    VNC_AUTH_TIGHT = 16,
+    VNC_AUTH_ULTRA = 17,
+    VNC_AUTH_TLS = 18,
+    VNC_AUTH_VENCRYPT = 19
+};
 
 struct VncState
 {
@@ -73,7 +96,13 @@ struct VncState
     int last_x;
     int last_y;
 
+    int major;
+    int minor;
+
     char *display;
+    char *password;
+    int auth;
+    char challenge[VNC_AUTH_CHALLENGE_SIZE];
 
     Buffer output;
     Buffer input;
@@ -1125,23 +1154,171 @@ static int protocol_client_init(VncState
     return 0;
 }
 
+static void make_challenge(VncState *vs)
+{
+    int i;
+
+    srand(time(NULL)+getpid()+getpid()*987654+rand());
+
+    for (i = 0 ; i < sizeof(vs->challenge) ; i++)
+        vs->challenge[i] = (int) (256.0*rand()/(RAND_MAX+1.0));
+}
+
+static int protocol_client_auth_vnc(VncState *vs, char *data, size_t len)
+{
+    char response[VNC_AUTH_CHALLENGE_SIZE];
+    int i, j, pwlen;
+    char key[8];
+
+    if (!vs->password || !vs->password[0]) {
+	VNC_DEBUG("No password configured on server");
+	vnc_write_u32(vs, 1); /* Reject auth */
+	if (vs->minor >= 8) {
+	    static const char err[] = "Authentication failed";
+	    vnc_write_u32(vs, sizeof(err));
+	    vnc_write(vs, err, sizeof(err));
+	}
+	vnc_flush(vs);
+	vnc_client_error(vs);
+	return 0;
+    }
+
+    memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE);
+
+    /* Calculate the expected challenge response */
+    pwlen = strlen(vs->password);
+    for (i=0; i<sizeof(key); i++)
+        key[i] = i<pwlen ? vs->password[i] : 0;
+    deskey(key, EN0);
+    for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8)
+        des(response+j, response+j);
+
+    /* Compare expected vs actual challenge response */
+    if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) {
+	VNC_DEBUG("Client challenge reponse did not match\n");
+	vnc_write_u32(vs, 1); /* Reject auth */
+	if (vs->minor >= 8) {
+	    static const char err[] = "Authentication failed";
+	    vnc_write_u32(vs, sizeof(err));
+	    vnc_write(vs, err, sizeof(err));
+	}
+	vnc_flush(vs);
+	vnc_client_error(vs);
+    } else {
+	VNC_DEBUG("Accepting VNC challenge response\n");
+	vnc_write_u32(vs, 0); /* Accept auth */
+	vnc_flush(vs);
+
+	vnc_read_when(vs, protocol_client_init, 1);
+    }
+    return 0;
+}
+
+static int start_auth_vnc(VncState *vs)
+{
+    make_challenge(vs);
+    /* Send client a 'random' challenge */
+    vnc_write(vs, vs->challenge, sizeof(vs->challenge));
+    vnc_flush(vs);
+
+    vnc_read_when(vs, protocol_client_auth_vnc, sizeof(vs->challenge));
+    return 0;
+}
+
+static int protocol_client_auth(VncState *vs, char *data, size_t len)
+{
+    /* We only advertise 1 auth scheme at a time, so client
+     * must pick the one we sent. Verify this */
+    if (data[0] != vs->auth) { /* Reject auth */
+       VNC_DEBUG("Reject auth %d\n", (int)data[0]);
+       vnc_write_u32(vs, 1);
+       if (vs->minor >= 8) {
+           static const char err[] = "Authentication failed";
+           vnc_write_u32(vs, sizeof(err));
+           vnc_write(vs, err, sizeof(err));
+       }
+       vnc_client_error(vs);
+    } else { /* Accept requested auth */
+       VNC_DEBUG("Client requested auth %d\n", (int)data[0]);
+       switch (vs->auth) {
+       case VNC_AUTH_NONE:
+           VNC_DEBUG("Accept auth none\n");
+           vnc_write_u32(vs, 0); /* Accept auth completion */
+           vnc_read_when(vs, protocol_client_init, 1);
+           break;
+
+       case VNC_AUTH_VNC:
+           VNC_DEBUG("Start VNC auth\n");
+           return start_auth_vnc(vs);
+
+       default: /* Should not be possible, but just in case */
+           VNC_DEBUG("Reject auth %d\n", vs->auth);
+           vnc_write_u8(vs, 1);
+           if (vs->minor >= 8) {
+               static const char err[] = "Authentication failed";
+               vnc_write_u32(vs, sizeof(err));
+               vnc_write(vs, err, sizeof(err));
+           }
+           vnc_client_error(vs);
+       }
+    }
+    return 0;
+}
+
 static int protocol_version(VncState *vs, char *version, size_t len)
 {
     char local[13];
-    int maj, min;
 
     memcpy(local, version, 12);
     local[12] = 0;
 
-    if (sscanf(local, "RFB %03d.%03d\n", &maj, &min) != 2) {
+    if (sscanf(local, "RFB %03d.%03d\n", &vs->major, &vs->minor) != 2) {
+	VNC_DEBUG("Malformed protocol version %s\n", local);
 	vnc_client_error(vs);
 	return 0;
     }
-
-    vnc_write_u32(vs, 1); /* None */
-    vnc_flush(vs);
-
-    vnc_read_when(vs, protocol_client_init, 1);
+    VNC_DEBUG("Client request protocol version %d.%d\n", vs->major, vs->minor);
+    if (vs->major != 3 ||
+	(vs->minor != 3 &&
+	 vs->minor != 5 &&
+	 vs->minor != 7 &&
+	 vs->minor != 8)) {
+	VNC_DEBUG("Unsupported client version\n");
+	vnc_write_u32(vs, VNC_AUTH_INVALID);
+	vnc_flush(vs);
+	vnc_client_error(vs);
+	return 0;
+    }
+    /* Some broken client report v3.5 which spec requires to be treated
+     * as equivalent to v3.3 by servers
+     */
+    if (vs->minor == 5)
+	vs->minor = 3;
+
+    if (vs->minor == 3) {
+	if (vs->auth == VNC_AUTH_NONE) {
+            VNC_DEBUG("Tell client auth none\n");
+            vnc_write_u32(vs, vs->auth);
+            vnc_flush(vs);
+            vnc_read_when(vs, protocol_client_init, 1);
+       } else if (vs->auth == VNC_AUTH_VNC) {
+            VNC_DEBUG("Tell client VNC auth\n");
+            vnc_write_u32(vs, vs->auth);
+            vnc_flush(vs);
+            start_auth_vnc(vs);
+       } else {
+            VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->auth);
+            vnc_write_u32(vs, VNC_AUTH_INVALID);
+            vnc_flush(vs);
+            vnc_client_error(vs);
+       }
+    } else {
+	VNC_DEBUG("Telling client we support auth %d\n", vs->auth);
+	vnc_write_u8(vs, 1); /* num auth */
+	vnc_write_u8(vs, vs->auth);
+	vnc_read_when(vs, protocol_client_auth, 1);
+	vnc_flush(vs);
+    }
 
     return 0;
 }
@@ -1156,7 +1333,7 @@ static void vnc_listen_read(void *opaque
     if (vs->csock != -1) {
         socket_set_nonblock(vs->csock);
 	qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, opaque);
-	vnc_write(vs, "RFB 003.003\n", 12);
+	vnc_write(vs, "RFB 003.008\n", 12);
 	vnc_flush(vs);
 	vnc_read_when(vs, protocol_version, 12);
 	memset(vs->old_data, 0, vs->ds->linesize * vs->ds->height);
@@ -1180,6 +1357,7 @@ void vnc_display_init(DisplayState *ds)
     ds->opaque = vs;
     vnc_state = vs;
     vs->display = NULL;
+    vs->password = NULL;
 
     vs->lsock = -1;
     vs->csock = -1;
@@ -1226,9 +1404,26 @@ void vnc_display_close(DisplayState *ds)
 	buffer_reset(&vs->output);
 	vs->need_update = 0;
     }
-}
-
-int vnc_display_open(DisplayState *ds, const char *arg)
+    vs->auth = VNC_AUTH_INVALID;
+}
+
+int vnc_display_password(DisplayState *ds, const char *password)
+{
+    VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
+
+    if (vs->password) {
+	qemu_free(vs->password);
+	vs->password = NULL;
+    }
+    if (password && password[0]) {
+	if (!(vs->password = qemu_strdup(password)))
+	    return -1;
+    }
+
+    return 0;
+}
+
+int vnc_display_open(DisplayState *ds, const char *display)
 {
     struct sockaddr *addr;
     struct sockaddr_in iaddr;
@@ -1239,15 +1434,32 @@ int vnc_display_open(DisplayState *ds, c
     socklen_t addrlen;
     const char *p;
     VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
+    const char *options;
+    int password = 0;
 
     vnc_display_close(ds);
-    if (strcmp(arg, "none") == 0)
+    if (strcmp(display, "none") == 0)
 	return 0;
 
-    if (!(vs->display = strdup(arg)))
+    if (!(vs->display = strdup(display)))
 	return -1;
+
+    options = display;
+    while ((options = strchr(options, ','))) {
+	options++;
+	if (strncmp(options, "password", 8) == 0)
+	    password = 1; /* Require password auth */
+    }
+
+    if (password) {
+	VNC_DEBUG("Initializing VNC server with password auth\n");
+	vs->auth = VNC_AUTH_VNC;
+    } else {
+	VNC_DEBUG("Initializing VNC server with no auth\n");
+	vs->auth = VNC_AUTH_NONE;
+    }
 #ifndef _WIN32
-    if (strstart(arg, "unix:", &p)) {
+    if (strstart(display, "unix:", &p)) {
 	addr = (struct sockaddr *)&uaddr;
 	addrlen = sizeof(uaddr);
 
@@ -1270,7 +1482,7 @@ int vnc_display_open(DisplayState *ds, c
 	addr = (struct sockaddr *)&iaddr;
 	addrlen = sizeof(iaddr);
 
-	if (parse_host_port(&iaddr, arg) < 0) {
+	if (parse_host_port(&iaddr, display) < 0) {
 	    fprintf(stderr, "Could not parse VNC address\n");
 	    free(vs->display);
 	    vs->display = NULL;

-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2007-08-13 19:44 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-07-31 19:23 [Qemu-devel] PATCH 0/8: Authentication support for the VNC server Daniel P. Berrange
2007-07-31 19:25 ` [Qemu-devel] PATCH 1/8: Refactor VNC server setup API Daniel P. Berrange
2007-07-31 19:25 ` [Qemu-devel] PATCH 2/8: Extend monitor 'change' command for VNC Daniel P. Berrange
2007-08-01  1:43   ` Anthony Liguori
2007-07-31 19:26 ` [Qemu-devel] PATCH 3/8: VNC password authentication Daniel P. Berrange
2007-08-01  1:46   ` Anthony Liguori
2007-08-01 16:26     ` Daniel P. Berrange
2007-08-02 14:35       ` Anthony Liguori
2007-07-31 19:27 ` [Qemu-devel] PATCH 4/8: VeNCrypt basic TLS support Daniel P. Berrange
2007-08-01  1:50   ` Anthony Liguori
2007-08-01 16:28     ` Daniel P. Berrange
2007-07-31 19:28 ` [Qemu-devel] PATCH 5/8: x509 certificate for server Daniel P. Berrange
2007-07-31 19:28 ` [Qemu-devel] PATCH 6/8: x509 client certificate verification Daniel P. Berrange
2007-07-31 19:29 ` [Qemu-devel] PATCH 7/8: command line args for x509 cert paths Daniel P. Berrange
2007-08-01  1:54   ` Anthony Liguori
2007-08-01 16:31     ` Daniel P. Berrange
2007-07-31 19:30 ` [Qemu-devel] PATCH 8/8: document all VNC authentication options Daniel P. Berrange
2007-08-01  1:55 ` [Qemu-devel] PATCH 0/8: Authentication support for the VNC server Anthony Liguori
  -- strict thread matches above, loose matches on Subject: below --
2007-08-13 19:25 Daniel P. Berrange
2007-08-13 19:44 ` [Qemu-devel] PATCH 3/8: VNC password authentication Daniel P. Berrange

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).