Commit 7084851534c834f00652f90a9da5e4032bd22130

Authored by ths
1 parent e25a5822

VNC password authentication, by Daniel P. Berrange.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3135 c046a42c-6fe2-441c-8c8c-71466251a162
Makefile.target
@@ -482,7 +482,7 @@ endif @@ -482,7 +482,7 @@ endif
482 ifdef CONFIG_SDL 482 ifdef CONFIG_SDL
483 VL_OBJS+=sdl.o x_keymap.o 483 VL_OBJS+=sdl.o x_keymap.o
484 endif 484 endif
485 -VL_OBJS+=vnc.o 485 +VL_OBJS+=vnc.o d3des.o
486 ifdef CONFIG_COCOA 486 ifdef CONFIG_COCOA
487 VL_OBJS+=cocoa.o 487 VL_OBJS+=cocoa.o
488 COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit 488 COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit
@@ -543,7 +543,7 @@ cocoa.o: cocoa.m @@ -543,7 +543,7 @@ cocoa.o: cocoa.m
543 sdl.o: sdl.c keymaps.c sdl_keysym.h 543 sdl.o: sdl.c keymaps.c sdl_keysym.h
544 $(CC) $(CFLAGS) $(CPPFLAGS) $(SDL_CFLAGS) $(BASE_CFLAGS) -c -o $@ $< 544 $(CC) $(CFLAGS) $(CPPFLAGS) $(SDL_CFLAGS) $(BASE_CFLAGS) -c -o $@ $<
545 545
546 -vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h 546 +vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h d3des.c d3des.h
547 $(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< 547 $(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
548 548
549 sdlaudio.o: sdlaudio.c 549 sdlaudio.o: sdlaudio.c
monitor.c
@@ -403,8 +403,17 @@ static void do_change_block(const char *device, const char *filename) @@ -403,8 +403,17 @@ static void do_change_block(const char *device, const char *filename)
403 403
404 static void do_change_vnc(const char *target) 404 static void do_change_vnc(const char *target)
405 { 405 {
406 - if (vnc_display_open(NULL, target) < 0)  
407 - term_printf("could not start VNC server on %s\n", target); 406 + if (strcmp(target, "passwd") == 0 ||
  407 + strcmp(target, "password") == 0) {
  408 + char password[9];
  409 + monitor_readline("Password: ", 1, password, sizeof(password)-1);
  410 + password[sizeof(password)-1] = '\0';
  411 + if (vnc_display_password(NULL, password) < 0)
  412 + term_printf("could not set VNC server password\n");
  413 + } else {
  414 + if (vnc_display_open(NULL, target) < 0)
  415 + term_printf("could not start VNC server on %s\n", target);
  416 + }
408 } 417 }
409 418
410 static void do_change(const char *device, const char *target) 419 static void do_change(const char *device, const char *target)
@@ -970,6 +970,7 @@ void cocoa_display_init(DisplayState *ds, int full_screen); @@ -970,6 +970,7 @@ void cocoa_display_init(DisplayState *ds, int full_screen);
970 void vnc_display_init(DisplayState *ds); 970 void vnc_display_init(DisplayState *ds);
971 void vnc_display_close(DisplayState *ds); 971 void vnc_display_close(DisplayState *ds);
972 int vnc_display_open(DisplayState *ds, const char *display); 972 int vnc_display_open(DisplayState *ds, const char *display);
  973 +int vnc_display_password(DisplayState *ds, const char *password);
973 void do_info_vnc(void); 974 void do_info_vnc(void);
974 975
975 /* x_keymap.c */ 976 /* x_keymap.c */
@@ -30,6 +30,15 @@ @@ -30,6 +30,15 @@
30 30
31 #include "vnc_keysym.h" 31 #include "vnc_keysym.h"
32 #include "keymaps.c" 32 #include "keymaps.c"
  33 +#include "d3des.h"
  34 +
  35 +// #define _VNC_DEBUG
  36 +
  37 +#ifdef _VNC_DEBUG
  38 +#define VNC_DEBUG(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
  39 +#else
  40 +#define VNC_DEBUG(fmt, ...) do { } while (0)
  41 +#endif
33 42
34 typedef struct Buffer 43 typedef struct Buffer
35 { 44 {
@@ -54,6 +63,20 @@ typedef void VncSendHextileTile(VncState *vs, @@ -54,6 +63,20 @@ typedef void VncSendHextileTile(VncState *vs,
54 #define VNC_MAX_HEIGHT 2048 63 #define VNC_MAX_HEIGHT 2048
55 #define VNC_DIRTY_WORDS (VNC_MAX_WIDTH / (16 * 32)) 64 #define VNC_DIRTY_WORDS (VNC_MAX_WIDTH / (16 * 32))
56 65
  66 +#define VNC_AUTH_CHALLENGE_SIZE 16
  67 +
  68 +enum {
  69 + VNC_AUTH_INVALID = 0,
  70 + VNC_AUTH_NONE = 1,
  71 + VNC_AUTH_VNC = 2,
  72 + VNC_AUTH_RA2 = 5,
  73 + VNC_AUTH_RA2NE = 6,
  74 + VNC_AUTH_TIGHT = 16,
  75 + VNC_AUTH_ULTRA = 17,
  76 + VNC_AUTH_TLS = 18,
  77 + VNC_AUTH_VENCRYPT = 19
  78 +};
  79 +
57 struct VncState 80 struct VncState
58 { 81 {
59 QEMUTimer *timer; 82 QEMUTimer *timer;
@@ -73,7 +96,13 @@ struct VncState @@ -73,7 +96,13 @@ struct VncState
73 int last_x; 96 int last_x;
74 int last_y; 97 int last_y;
75 98
  99 + int major;
  100 + int minor;
  101 +
76 char *display; 102 char *display;
  103 + char *password;
  104 + int auth;
  105 + char challenge[VNC_AUTH_CHALLENGE_SIZE];
77 106
78 Buffer output; 107 Buffer output;
79 Buffer input; 108 Buffer input;
@@ -1125,23 +1154,171 @@ static int protocol_client_init(VncState *vs, char *data, size_t len) @@ -1125,23 +1154,171 @@ static int protocol_client_init(VncState *vs, char *data, size_t len)
1125 return 0; 1154 return 0;
1126 } 1155 }
1127 1156
  1157 +static void make_challenge(VncState *vs)
  1158 +{
  1159 + int i;
  1160 +
  1161 + srand(time(NULL)+getpid()+getpid()*987654+rand());
  1162 +
  1163 + for (i = 0 ; i < sizeof(vs->challenge) ; i++)
  1164 + vs->challenge[i] = (int) (256.0*rand()/(RAND_MAX+1.0));
  1165 +}
  1166 +
  1167 +static int protocol_client_auth_vnc(VncState *vs, char *data, size_t len)
  1168 +{
  1169 + char response[VNC_AUTH_CHALLENGE_SIZE];
  1170 + int i, j, pwlen;
  1171 + char key[8];
  1172 +
  1173 + if (!vs->password || !vs->password[0]) {
  1174 + VNC_DEBUG("No password configured on server");
  1175 + vnc_write_u32(vs, 1); /* Reject auth */
  1176 + if (vs->minor >= 8) {
  1177 + static const char err[] = "Authentication failed";
  1178 + vnc_write_u32(vs, sizeof(err));
  1179 + vnc_write(vs, err, sizeof(err));
  1180 + }
  1181 + vnc_flush(vs);
  1182 + vnc_client_error(vs);
  1183 + return 0;
  1184 + }
  1185 +
  1186 + memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE);
  1187 +
  1188 + /* Calculate the expected challenge response */
  1189 + pwlen = strlen(vs->password);
  1190 + for (i=0; i<sizeof(key); i++)
  1191 + key[i] = i<pwlen ? vs->password[i] : 0;
  1192 + deskey(key, EN0);
  1193 + for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8)
  1194 + des(response+j, response+j);
  1195 +
  1196 + /* Compare expected vs actual challenge response */
  1197 + if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) {
  1198 + VNC_DEBUG("Client challenge reponse did not match\n");
  1199 + vnc_write_u32(vs, 1); /* Reject auth */
  1200 + if (vs->minor >= 8) {
  1201 + static const char err[] = "Authentication failed";
  1202 + vnc_write_u32(vs, sizeof(err));
  1203 + vnc_write(vs, err, sizeof(err));
  1204 + }
  1205 + vnc_flush(vs);
  1206 + vnc_client_error(vs);
  1207 + } else {
  1208 + VNC_DEBUG("Accepting VNC challenge response\n");
  1209 + vnc_write_u32(vs, 0); /* Accept auth */
  1210 + vnc_flush(vs);
  1211 +
  1212 + vnc_read_when(vs, protocol_client_init, 1);
  1213 + }
  1214 + return 0;
  1215 +}
  1216 +
  1217 +static int start_auth_vnc(VncState *vs)
  1218 +{
  1219 + make_challenge(vs);
  1220 + /* Send client a 'random' challenge */
  1221 + vnc_write(vs, vs->challenge, sizeof(vs->challenge));
  1222 + vnc_flush(vs);
  1223 +
  1224 + vnc_read_when(vs, protocol_client_auth_vnc, sizeof(vs->challenge));
  1225 + return 0;
  1226 +}
  1227 +
  1228 +static int protocol_client_auth(VncState *vs, char *data, size_t len)
  1229 +{
  1230 + /* We only advertise 1 auth scheme at a time, so client
  1231 + * must pick the one we sent. Verify this */
  1232 + if (data[0] != vs->auth) { /* Reject auth */
  1233 + VNC_DEBUG("Reject auth %d\n", (int)data[0]);
  1234 + vnc_write_u32(vs, 1);
  1235 + if (vs->minor >= 8) {
  1236 + static const char err[] = "Authentication failed";
  1237 + vnc_write_u32(vs, sizeof(err));
  1238 + vnc_write(vs, err, sizeof(err));
  1239 + }
  1240 + vnc_client_error(vs);
  1241 + } else { /* Accept requested auth */
  1242 + VNC_DEBUG("Client requested auth %d\n", (int)data[0]);
  1243 + switch (vs->auth) {
  1244 + case VNC_AUTH_NONE:
  1245 + VNC_DEBUG("Accept auth none\n");
  1246 + vnc_write_u32(vs, 0); /* Accept auth completion */
  1247 + vnc_read_when(vs, protocol_client_init, 1);
  1248 + break;
  1249 +
  1250 + case VNC_AUTH_VNC:
  1251 + VNC_DEBUG("Start VNC auth\n");
  1252 + return start_auth_vnc(vs);
  1253 +
  1254 + default: /* Should not be possible, but just in case */
  1255 + VNC_DEBUG("Reject auth %d\n", vs->auth);
  1256 + vnc_write_u8(vs, 1);
  1257 + if (vs->minor >= 8) {
  1258 + static const char err[] = "Authentication failed";
  1259 + vnc_write_u32(vs, sizeof(err));
  1260 + vnc_write(vs, err, sizeof(err));
  1261 + }
  1262 + vnc_client_error(vs);
  1263 + }
  1264 + }
  1265 + return 0;
  1266 +}
  1267 +
1128 static int protocol_version(VncState *vs, char *version, size_t len) 1268 static int protocol_version(VncState *vs, char *version, size_t len)
1129 { 1269 {
1130 char local[13]; 1270 char local[13];
1131 - int maj, min;  
1132 1271
1133 memcpy(local, version, 12); 1272 memcpy(local, version, 12);
1134 local[12] = 0; 1273 local[12] = 0;
1135 1274
1136 - if (sscanf(local, "RFB %03d.%03d\n", &maj, &min) != 2) { 1275 + if (sscanf(local, "RFB %03d.%03d\n", &vs->major, &vs->minor) != 2) {
  1276 + VNC_DEBUG("Malformed protocol version %s\n", local);
1137 vnc_client_error(vs); 1277 vnc_client_error(vs);
1138 return 0; 1278 return 0;
1139 } 1279 }
1140 -  
1141 - vnc_write_u32(vs, 1); /* None */  
1142 - vnc_flush(vs);  
1143 -  
1144 - vnc_read_when(vs, protocol_client_init, 1); 1280 + VNC_DEBUG("Client request protocol version %d.%d\n", vs->major, vs->minor);
  1281 + if (vs->major != 3 ||
  1282 + (vs->minor != 3 &&
  1283 + vs->minor != 5 &&
  1284 + vs->minor != 7 &&
  1285 + vs->minor != 8)) {
  1286 + VNC_DEBUG("Unsupported client version\n");
  1287 + vnc_write_u32(vs, VNC_AUTH_INVALID);
  1288 + vnc_flush(vs);
  1289 + vnc_client_error(vs);
  1290 + return 0;
  1291 + }
  1292 + /* Some broken client report v3.5 which spec requires to be treated
  1293 + * as equivalent to v3.3 by servers
  1294 + */
  1295 + if (vs->minor == 5)
  1296 + vs->minor = 3;
  1297 +
  1298 + if (vs->minor == 3) {
  1299 + if (vs->auth == VNC_AUTH_NONE) {
  1300 + VNC_DEBUG("Tell client auth none\n");
  1301 + vnc_write_u32(vs, vs->auth);
  1302 + vnc_flush(vs);
  1303 + vnc_read_when(vs, protocol_client_init, 1);
  1304 + } else if (vs->auth == VNC_AUTH_VNC) {
  1305 + VNC_DEBUG("Tell client VNC auth\n");
  1306 + vnc_write_u32(vs, vs->auth);
  1307 + vnc_flush(vs);
  1308 + start_auth_vnc(vs);
  1309 + } else {
  1310 + VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->auth);
  1311 + vnc_write_u32(vs, VNC_AUTH_INVALID);
  1312 + vnc_flush(vs);
  1313 + vnc_client_error(vs);
  1314 + }
  1315 + } else {
  1316 + VNC_DEBUG("Telling client we support auth %d\n", vs->auth);
  1317 + vnc_write_u8(vs, 1); /* num auth */
  1318 + vnc_write_u8(vs, vs->auth);
  1319 + vnc_read_when(vs, protocol_client_auth, 1);
  1320 + vnc_flush(vs);
  1321 + }
1145 1322
1146 return 0; 1323 return 0;
1147 } 1324 }
@@ -1156,7 +1333,7 @@ static void vnc_listen_read(void *opaque) @@ -1156,7 +1333,7 @@ static void vnc_listen_read(void *opaque)
1156 if (vs->csock != -1) { 1333 if (vs->csock != -1) {
1157 socket_set_nonblock(vs->csock); 1334 socket_set_nonblock(vs->csock);
1158 qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, opaque); 1335 qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, opaque);
1159 - vnc_write(vs, "RFB 003.003\n", 12); 1336 + vnc_write(vs, "RFB 003.008\n", 12);
1160 vnc_flush(vs); 1337 vnc_flush(vs);
1161 vnc_read_when(vs, protocol_version, 12); 1338 vnc_read_when(vs, protocol_version, 12);
1162 memset(vs->old_data, 0, vs->ds->linesize * vs->ds->height); 1339 memset(vs->old_data, 0, vs->ds->linesize * vs->ds->height);
@@ -1180,6 +1357,7 @@ void vnc_display_init(DisplayState *ds) @@ -1180,6 +1357,7 @@ void vnc_display_init(DisplayState *ds)
1180 ds->opaque = vs; 1357 ds->opaque = vs;
1181 vnc_state = vs; 1358 vnc_state = vs;
1182 vs->display = NULL; 1359 vs->display = NULL;
  1360 + vs->password = NULL;
1183 1361
1184 vs->lsock = -1; 1362 vs->lsock = -1;
1185 vs->csock = -1; 1363 vs->csock = -1;
@@ -1227,9 +1405,26 @@ void vnc_display_close(DisplayState *ds) @@ -1227,9 +1405,26 @@ void vnc_display_close(DisplayState *ds)
1227 buffer_reset(&vs->output); 1405 buffer_reset(&vs->output);
1228 vs->need_update = 0; 1406 vs->need_update = 0;
1229 } 1407 }
  1408 + vs->auth = VNC_AUTH_INVALID;
  1409 +}
  1410 +
  1411 +int vnc_display_password(DisplayState *ds, const char *password)
  1412 +{
  1413 + VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
  1414 +
  1415 + if (vs->password) {
  1416 + qemu_free(vs->password);
  1417 + vs->password = NULL;
  1418 + }
  1419 + if (password && password[0]) {
  1420 + if (!(vs->password = qemu_strdup(password)))
  1421 + return -1;
  1422 + }
  1423 +
  1424 + return 0;
1230 } 1425 }
1231 1426
1232 -int vnc_display_open(DisplayState *ds, const char *arg) 1427 +int vnc_display_open(DisplayState *ds, const char *display)
1233 { 1428 {
1234 struct sockaddr *addr; 1429 struct sockaddr *addr;
1235 struct sockaddr_in iaddr; 1430 struct sockaddr_in iaddr;
@@ -1240,15 +1435,32 @@ int vnc_display_open(DisplayState *ds, const char *arg) @@ -1240,15 +1435,32 @@ int vnc_display_open(DisplayState *ds, const char *arg)
1240 socklen_t addrlen; 1435 socklen_t addrlen;
1241 const char *p; 1436 const char *p;
1242 VncState *vs = ds ? (VncState *)ds->opaque : vnc_state; 1437 VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
  1438 + const char *options;
  1439 + int password = 0;
1243 1440
1244 vnc_display_close(ds); 1441 vnc_display_close(ds);
1245 - if (strcmp(arg, "none") == 0) 1442 + if (strcmp(display, "none") == 0)
1246 return 0; 1443 return 0;
1247 1444
1248 - if (!(vs->display = strdup(arg))) 1445 + if (!(vs->display = strdup(display)))
1249 return -1; 1446 return -1;
  1447 +
  1448 + options = display;
  1449 + while ((options = strchr(options, ','))) {
  1450 + options++;
  1451 + if (strncmp(options, "password", 8) == 0)
  1452 + password = 1; /* Require password auth */
  1453 + }
  1454 +
  1455 + if (password) {
  1456 + VNC_DEBUG("Initializing VNC server with password auth\n");
  1457 + vs->auth = VNC_AUTH_VNC;
  1458 + } else {
  1459 + VNC_DEBUG("Initializing VNC server with no auth\n");
  1460 + vs->auth = VNC_AUTH_NONE;
  1461 + }
1250 #ifndef _WIN32 1462 #ifndef _WIN32
1251 - if (strstart(arg, "unix:", &p)) { 1463 + if (strstart(display, "unix:", &p)) {
1252 addr = (struct sockaddr *)&uaddr; 1464 addr = (struct sockaddr *)&uaddr;
1253 addrlen = sizeof(uaddr); 1465 addrlen = sizeof(uaddr);
1254 1466
@@ -1271,7 +1483,7 @@ int vnc_display_open(DisplayState *ds, const char *arg) @@ -1271,7 +1483,7 @@ int vnc_display_open(DisplayState *ds, const char *arg)
1271 addr = (struct sockaddr *)&iaddr; 1483 addr = (struct sockaddr *)&iaddr;
1272 addrlen = sizeof(iaddr); 1484 addrlen = sizeof(iaddr);
1273 1485
1274 - if (parse_host_port(&iaddr, arg) < 0) { 1486 + if (parse_host_port(&iaddr, display) < 0) {
1275 fprintf(stderr, "Could not parse VNC address\n"); 1487 fprintf(stderr, "Could not parse VNC address\n");
1276 free(vs->display); 1488 free(vs->display);
1277 vs->display = NULL; 1489 vs->display = NULL;