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 482 ifdef CONFIG_SDL
483 483 VL_OBJS+=sdl.o x_keymap.o
484 484 endif
485   -VL_OBJS+=vnc.o
  485 +VL_OBJS+=vnc.o d3des.o
486 486 ifdef CONFIG_COCOA
487 487 VL_OBJS+=cocoa.o
488 488 COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit
... ... @@ -543,7 +543,7 @@ cocoa.o: cocoa.m
543 543 sdl.o: sdl.c keymaps.c sdl_keysym.h
544 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 547 $(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
548 548  
549 549 sdlaudio.o: sdlaudio.c
... ...
monitor.c
... ... @@ -403,8 +403,17 @@ static void do_change_block(const char *device, const char *filename)
403 403  
404 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 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 970 void vnc_display_init(DisplayState *ds);
971 971 void vnc_display_close(DisplayState *ds);
972 972 int vnc_display_open(DisplayState *ds, const char *display);
  973 +int vnc_display_password(DisplayState *ds, const char *password);
973 974 void do_info_vnc(void);
974 975  
975 976 /* x_keymap.c */
... ...
... ... @@ -30,6 +30,15 @@
30 30  
31 31 #include "vnc_keysym.h"
32 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 43 typedef struct Buffer
35 44 {
... ... @@ -54,6 +63,20 @@ typedef void VncSendHextileTile(VncState *vs,
54 63 #define VNC_MAX_HEIGHT 2048
55 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 80 struct VncState
58 81 {
59 82 QEMUTimer *timer;
... ... @@ -73,7 +96,13 @@ struct VncState
73 96 int last_x;
74 97 int last_y;
75 98  
  99 + int major;
  100 + int minor;
  101 +
76 102 char *display;
  103 + char *password;
  104 + int auth;
  105 + char challenge[VNC_AUTH_CHALLENGE_SIZE];
77 106  
78 107 Buffer output;
79 108 Buffer input;
... ... @@ -1125,23 +1154,171 @@ static int protocol_client_init(VncState *vs, char *data, size_t len)
1125 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 1268 static int protocol_version(VncState *vs, char *version, size_t len)
1129 1269 {
1130 1270 char local[13];
1131   - int maj, min;
1132 1271  
1133 1272 memcpy(local, version, 12);
1134 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 1277 vnc_client_error(vs);
1138 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 1323 return 0;
1147 1324 }
... ... @@ -1156,7 +1333,7 @@ static void vnc_listen_read(void *opaque)
1156 1333 if (vs->csock != -1) {
1157 1334 socket_set_nonblock(vs->csock);
1158 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 1337 vnc_flush(vs);
1161 1338 vnc_read_when(vs, protocol_version, 12);
1162 1339 memset(vs->old_data, 0, vs->ds->linesize * vs->ds->height);
... ... @@ -1180,6 +1357,7 @@ void vnc_display_init(DisplayState *ds)
1180 1357 ds->opaque = vs;
1181 1358 vnc_state = vs;
1182 1359 vs->display = NULL;
  1360 + vs->password = NULL;
1183 1361  
1184 1362 vs->lsock = -1;
1185 1363 vs->csock = -1;
... ... @@ -1227,9 +1405,26 @@ void vnc_display_close(DisplayState *ds)
1227 1405 buffer_reset(&vs->output);
1228 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 1429 struct sockaddr *addr;
1235 1430 struct sockaddr_in iaddr;
... ... @@ -1240,15 +1435,32 @@ int vnc_display_open(DisplayState *ds, const char *arg)
1240 1435 socklen_t addrlen;
1241 1436 const char *p;
1242 1437 VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
  1438 + const char *options;
  1439 + int password = 0;
1243 1440  
1244 1441 vnc_display_close(ds);
1245   - if (strcmp(arg, "none") == 0)
  1442 + if (strcmp(display, "none") == 0)
1246 1443 return 0;
1247 1444  
1248   - if (!(vs->display = strdup(arg)))
  1445 + if (!(vs->display = strdup(display)))
1249 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 1462 #ifndef _WIN32
1251   - if (strstart(arg, "unix:", &p)) {
  1463 + if (strstart(display, "unix:", &p)) {
1252 1464 addr = (struct sockaddr *)&uaddr;
1253 1465 addrlen = sizeof(uaddr);
1254 1466  
... ... @@ -1271,7 +1483,7 @@ int vnc_display_open(DisplayState *ds, const char *arg)
1271 1483 addr = (struct sockaddr *)&iaddr;
1272 1484 addrlen = sizeof(iaddr);
1273 1485  
1274   - if (parse_host_port(&iaddr, arg) < 0) {
  1486 + if (parse_host_port(&iaddr, display) < 0) {
1275 1487 fprintf(stderr, "Could not parse VNC address\n");
1276 1488 free(vs->display);
1277 1489 vs->display = NULL;
... ...