Index: usr.sbin/bhyve/Makefile =================================================================== --- usr.sbin/bhyve/Makefile +++ usr.sbin/bhyve/Makefile @@ -61,7 +61,7 @@ .PATH: ${BHYVE_SYSDIR}/sys/amd64/vmm SRCS+= vmm_instruction_emul.c -LIBADD= vmmapi md pthread z +LIBADD= vmmapi md pthread z crypto CFLAGS+= -I${BHYVE_SYSDIR}/sys/dev/e1000 CFLAGS+= -I${BHYVE_SYSDIR}/sys/dev/mii Index: usr.sbin/bhyve/bhyve.8 =================================================================== --- usr.sbin/bhyve/bhyve.8 +++ usr.sbin/bhyve/bhyve.8 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 3, 2017 +.Dd May 19, 2017 .Dt BHYVE 8 .Os .Sh NAME @@ -309,7 +309,7 @@ .Pp Framebuffer devices: .Bl -tag -width 10n -.It Oo rfb= Ns Oo Ar IP: Oc Ns Ar port Oc Ns Oo ,w= Ns Ar width Oc Ns Oo ,h= Ns Ar height Oc Ns Oo ,vga= Ns Ar vgaconf Oc Ns Oo Ns ,wait Oc +.It Oo rfb= Ns Oo Ar IP: Oc Ns Ar port Oc Ns Oo ,w= Ns Ar width Oc Ns Oo ,h= Ns Ar height Oc Ns Oo ,vga= Ns Ar vgaconf Oc Ns Oo Ns ,wait Oc Ns Oo ,password= Ns Ar password Oc .Bl -tag -width 8n .It Ar IP:port An @@ -368,6 +368,9 @@ to only boot upon the initiation of a VNC connection, simplifying the installation of operating systems that require immediate keyboard input. This can be removed for post-installation use. +.It password +The password implementation usage is only for interoperability with certain VNC +clients and must not be used as a method to secure the VNC service. .El .El .Pp Index: usr.sbin/bhyve/pci_fbuf.c =================================================================== --- usr.sbin/bhyve/pci_fbuf.c +++ usr.sbin/bhyve/pci_fbuf.c @@ -93,6 +93,7 @@ /* rfb server */ char *rfb_host; + char *rfb_password; int rfb_port; int rfb_wait; int vga_enabled; @@ -285,7 +286,8 @@ goto done; } else if (sc->memregs.height == 0) sc->memregs.height = 1080; - + } else if (!strcmp(xopts, "password")) { + sc->rfb_password = config; } else { pci_fbuf_usage(xopts); ret = -1; @@ -407,7 +409,7 @@ memset((void *)sc->fb_base, 0, FB_SIZE); - error = rfb_init(sc->rfb_host, sc->rfb_port, sc->rfb_wait); + error = rfb_init(sc->rfb_host, sc->rfb_port, sc->rfb_wait, sc->rfb_password); done: if (error) free(sc); Index: usr.sbin/bhyve/rfb.h =================================================================== --- usr.sbin/bhyve/rfb.h +++ usr.sbin/bhyve/rfb.h @@ -31,6 +31,12 @@ #define RFB_PORT 5900 -int rfb_init(char *hostname, int port, int wait); +int rfb_init(char *hostname, int port, int wait, char *password); + +#define SECURITY_TYPE_NONE 1 +#define SECURITY_TYPE_VNC_AUTH 2 + +#define AUTH_FAILED_UNAUTH 1 +#define AUTH_FAILED_ERROR 2 #endif /* _RFB_H_ */ Index: usr.sbin/bhyve/rfb.c =================================================================== --- usr.sbin/bhyve/rfb.c +++ usr.sbin/bhyve/rfb.c @@ -60,10 +60,14 @@ #include "rfb.h" #include "sockstream.h" +#include + static int rfb_debug = 0; #define DPRINTF(params) if (rfb_debug) printf params #define WPRINTF(params) printf params +#define AUTH_LENGTH 16 + struct rfb_softc { int sfd; pthread_t tid; @@ -72,10 +76,15 @@ int width, height; - bool enc_raw_ok; - bool enc_zlib_ok; - bool enc_resize_ok; + char *password; + unsigned char challenge[AUTH_LENGTH]; + unsigned char keystr[AUTH_LENGTH]; + unsigned char crypt_expected[AUTH_LENGTH]; + bool enc_raw_ok; + bool enc_zlib_ok; + bool enc_resize_ok; + z_stream zstream; uint8_t *zbuf; int zbuflen; @@ -739,9 +748,13 @@ { const char *vbuf = "RFB 003.008\n"; unsigned char buf[80]; + unsigned char *message; + + DES_key_schedule ks; pthread_t tid; - uint32_t sres; + uint32_t sres; int len; + int i; rc->cfd = cfd; @@ -753,17 +766,75 @@ /* 2a. Send security type 'none' */ buf[0] = 1; - buf[1] = 1; /* none */ + if (rc->password) + buf[1] = SECURITY_TYPE_VNC_AUTH; + else + buf[1] = SECURITY_TYPE_NONE; + stream_write(cfd, buf, 2); - /* 2b. Read agreed security type */ len = stream_read(cfd, buf, 1); - /* 2c. Write back a status of 0 */ - sres = 0; + /* 2c. Do VNC authentication */ + switch (buf[0]) { + case SECURITY_TYPE_NONE: + sres = 0; + break; + case SECURITY_TYPE_VNC_AUTH: + /* + * The client encrypts the challenge with DES, using a password + * supplied by the user as the key. To form the key, the password + * is truncated to eight characters, or padded with null bytes on + * the right. The client then sends the resulting 16-bytes response. + */ + strncpy(rc->keystr, rc->password, AUTH_LENGTH); + + /* VNC clients encrypts the challenge with all the bit fields in each + * byte of the password mirrored. Here we flip each byte of the keystr. + */ + for (i = 0; i < 16; i++) { + rc->keystr[i] = (rc->keystr[i] & 0xF0) >> 4 + | (rc->keystr[i] & 0x0F) << 4; + rc->keystr[i] = (rc->keystr[i] & 0xCC) >> 2 + | (rc->keystr[i] & 0x33) << 2; + rc->keystr[i] = (rc->keystr[i] & 0xAA) >> 1 + | (rc->keystr[i] & 0x55) << 1; + } + + /* Initialize a 16-byte random challenge */ + arc4random_buf(rc->challenge, sizeof(rc->challenge)); + stream_write(cfd, rc->challenge, AUTH_LENGTH); + + /* Receive the 16-byte challenge response */ + stream_read(cfd, buf, AUTH_LENGTH); + + /* We encrypt the challenge sent with the flipped keystr. */ + DES_set_key((C_Block *)rc->keystr, &ks); + DES_ecb_encrypt((C_Block *)rc->challenge, + (C_Block *)rc->crypt_expected, &ks, DES_ENCRYPT); + DES_ecb_encrypt((C_Block *)(rc->challenge + 8), + (C_Block *)(rc->crypt_expected + 8), &ks, DES_ENCRYPT); + + if (memcmp(buf, rc->crypt_expected, AUTH_LENGTH)) { + message = "Auth Failed: Invalid Password."; + sres = htonl(1); + } else + sres = 0; + + break; + } + + /* 2d. Write back a status */ stream_write(cfd, &sres, 4); + if (sres) { + *((uint32_t *) buf) = htonl(strlen(message)); + strncpy(buf + 4, message, strlen(message) + 1); + stream_write(cfd, buf, 4 + strlen(message)); + goto done; + } + /* 3a. Read client shared-flag byte */ len = stream_read(cfd, buf, 1); @@ -869,7 +940,7 @@ } int -rfb_init(char *hostname, int port, int wait) +rfb_init(char *hostname, int port, int wait, char *password) { struct rfb_softc *rc; struct sockaddr_in sin; @@ -886,6 +957,8 @@ sizeof(uint32_t)); rc->crc_width = RFB_MAX_WIDTH; rc->crc_height = RFB_MAX_HEIGHT; + + rc->password = password; rc->sfd = socket(AF_INET, SOCK_STREAM, 0); if (rc->sfd < 0) {