VNC password authentication, by Daniel P. Berrange.

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3135 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
ths 2007-08-25 01:37:05 +00:00
parent e25a5822ca
commit 7084851534
4 changed files with 240 additions and 18 deletions

View File

@ -482,7 +482,7 @@ endif
ifdef CONFIG_SDL
VL_OBJS+=sdl.o x_keymap.o
endif
VL_OBJS+=vnc.o
VL_OBJS+=vnc.o d3des.o
ifdef CONFIG_COCOA
VL_OBJS+=cocoa.o
COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit
@ -543,7 +543,7 @@ cocoa.o: cocoa.m
sdl.o: sdl.c keymaps.c sdl_keysym.h
$(CC) $(CFLAGS) $(CPPFLAGS) $(SDL_CFLAGS) $(BASE_CFLAGS) -c -o $@ $<
vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h
vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h d3des.c d3des.h
$(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
sdlaudio.o: sdlaudio.c

View File

@ -403,8 +403,17 @@ static void do_change_block(const char *device, const char *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);
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)

1
vl.h
View File

@ -970,6 +970,7 @@ void cocoa_display_init(DisplayState *ds, int full_screen);
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 */

240
vnc.c
View File

@ -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
{
@ -54,6 +63,20 @@ typedef void VncSendHextileTile(VncState *vs,
#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
{
QEMUTimer *timer;
@ -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 *vs, char *data, size_t len)
return 0;
}
static int protocol_version(VncState *vs, char *version, size_t len)
static void make_challenge(VncState *vs)
{
char local[13];
int maj, min;
int i;
memcpy(local, version, 12);
local[12] = 0;
srand(time(NULL)+getpid()+getpid()*987654+rand());
if (sscanf(local, "RFB %03d.%03d\n", &maj, &min) != 2) {
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;
}
vnc_write_u32(vs, 1); /* None */
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_init, 1);
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];
memcpy(local, version, 12);
local[12] = 0;
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_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;
@ -1227,9 +1405,26 @@ void vnc_display_close(DisplayState *ds)
buffer_reset(&vs->output);
vs->need_update = 0;
}
vs->auth = VNC_AUTH_INVALID;
}
int vnc_display_open(DisplayState *ds, const char *arg)
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;
@ -1240,15 +1435,32 @@ int vnc_display_open(DisplayState *ds, const char *arg)
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);
@ -1271,7 +1483,7 @@ int vnc_display_open(DisplayState *ds, const char *arg)
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;