From d8b7f7a7d6c283e85c545c16fe6a598161cf306a Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Thu, 25 Aug 2011 15:32:48 -0500 Subject: Add sha1.*. Remove UTF-8 encode. Protocol handling. Add common/sha1.h and common/sha1.c so that we have the SHA routines even if openssl is not available. From the IETF SHA RFC example code. Remove the UTF-8 encoding hack. This was really just an experiment. If the protocol passed in the handshake has "binary" then don't base64 encode for the HyBi protocol. This will allow noVNC to request the binary data be passed raw and not base64 encoded. Unfortunately, the client doesn't speak first in VNC protocol (bad original design). If it did then we could determine whether to base64 encode or not based on the first HyBi frame from the client and whether the binary bit is set or not. Oh well. Misc Cleanup: - Always free response and buf in handshake routine. - Remove some unused variables. --- libvncserver/Makefile.am | 2 +- libvncserver/websockets.c | 300 +++++++++++++++++++--------------------------- 2 files changed, 126 insertions(+), 176 deletions(-) (limited to 'libvncserver') diff --git a/libvncserver/Makefile.am b/libvncserver/Makefile.am index a9feac1..4a031af 100644 --- a/libvncserver/Makefile.am +++ b/libvncserver/Makefile.am @@ -24,7 +24,7 @@ WEBSOCKETSSSLSRCS = rfbssl_none.c #endif endif -WEBSOCKETSSRCS = websockets.c ../common/md5.c $(WEBSOCKETSSSLSRCS) +WEBSOCKETSSRCS = websockets.c ../common/md5.c ../common/sha1.c $(WEBSOCKETSSSLSRCS) endif includedir=$(prefix)/include/rfb diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index da00522..0cce3c9 100755 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -31,8 +31,10 @@ /* errno */ #include -#include #include +#include +#include "md5.h" +#include "sha1.h" #include "rfbconfig.h" #include "rfbssl.h" @@ -58,10 +60,12 @@ enum { WEBSOCKETS_VERSION_HYBI }; +#if 0 #include static int gettid() { return (int)syscall(SYS_gettid); } +#endif typedef int (*wsEncodeFunc)(rfbClientPtr cl, const char *src, int len, char **dst); typedef int (*wsDecodeFunc)(rfbClientPtr cl, char *dst, int len); @@ -162,23 +166,36 @@ min (int a, int b) { return a < b ? a : b; } -#ifdef LIBVNCSERVER_WITH_CLIENT_GCRYPT -#else -#include -static void webSocketsGenSha1Key(char *target, int size, char *key) +void +webSocketsGenSha1Key(char * target, int size, char *key) { - SHA_CTX c; - unsigned char tmp[SHA_DIGEST_LENGTH]; - - SHA1_Init(&c); - SHA1_Update(&c, key, strlen(key)); - SHA1_Update(&c, GUID, sizeof(GUID) - 1); - SHA1_Final(tmp, &c); - if (-1 == __b64_ntop(tmp, SHA_DIGEST_LENGTH, target, size)) - rfbErr("b64_ntop failed\n"); + int len; + SHA1Context sha; + uint8_t digest[SHA1HashSize]; + + if (size < B64LEN(SHA1HashSize) + 1) { + rfbErr("webSocketsGenSha1Key: not enough space in target\n"); + target[0] = '\0'; + return; + } + + SHA1Reset(&sha); + SHA1Input(&sha, (unsigned char *)key, strlen(key)); + SHA1Input(&sha, (unsigned char *)GUID, strlen(GUID)); + SHA1Result(&sha, digest); + + len = __b64_ntop((unsigned char *)digest, SHA1HashSize, target, size); + if (len < size - 1) { + rfbErr("webSocketsGenSha1Key: b64_ntop failed\n"); + target[0] = '\0'; + return; + } + + target[len] = '\0'; + return; } -#endif + /* * rfbWebSocketsHandshake is called to handle new WebSockets connections @@ -237,7 +254,7 @@ static rfbBool webSocketsHandshake(rfbClientPtr cl, char *scheme) { char *buf, *response, *line; - int n, linestart = 0, len = 0, llen, base64 = 0; + int n, linestart = 0, len = 0, llen, base64 = TRUE; char prefix[5], trailer[17]; char *path = NULL, *host = NULL, *origin = NULL, *protocol = NULL; char *key1 = NULL, *key2 = NULL, *key3 = NULL; @@ -268,6 +285,8 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) rfbLog("webSocketsHandshake: client gone\n"); else rfbLogPerror("webSocketsHandshake: read"); + free(response); + free(buf); return FALSE; } @@ -285,6 +304,8 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) rfbLog("webSocketsHandshake: client gone\n"); else rfbLogPerror("webSocketsHandshake: read"); + free(response); + free(buf); return FALSE; } rfbLog("Got key3\n"); @@ -298,7 +319,6 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) /* 16 = 4 ("GET ") + 1 ("/.*") + 11 (" HTTP/1.1\r\n") */ path = line+4; buf[len-11] = '\0'; /* Trim trailing " HTTP/1.1\r\n" */ - base64 = TRUE; cl->wspath = strdup(path); /* rfbLog("Got path: %s\n", path); */ } else if ((strncasecmp("host: ", line, min(llen,6))) == 0) { @@ -345,14 +365,25 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) return FALSE; } - /* - if ((!protocol) || (!strcasestr(protocol, "base64"))) { - rfbErr("webSocketsHandshake: base64 subprotocol not supported by client\n"); - free(response); - free(buf); - return FALSE; + if ((protocol) && (strstr(protocol, "binary"))) { + if (! sec_ws_version) { + rfbErr("webSocketsHandshake: 'binary' protocol not supported with Hixie\n"); + free(response); + free(buf); + return FALSE; + } + rfbLog(" - webSocketsHandshake: using binary/raw encoding\n"); + base64 = FALSE; + protocol = "binary"; + } else { + rfbLog(" - webSocketsHandshake: using base64 encoding\n"); + base64 = TRUE; + if ((protocol) && (strstr(protocol, "base64"))) { + protocol = "base64"; + } else { + protocol = ""; + } } - */ /* * Generate the WebSockets server response based on the the headers sent @@ -360,7 +391,7 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) */ if (sec_ws_version) { - char accept[SHA_DIGEST_LENGTH * 3]; + char accept[B64LEN(SHA1HashSize) + 1]; rfbLog(" - WebSockets client version hybi-%02d\n", sec_ws_version); webSocketsGenSha1Key(accept, sizeof(accept), sec_ws_key); len = snprintf(response, WEBSOCKETS_MAX_HANDSHAKE_LEN, @@ -457,38 +488,16 @@ webSocketsGenMd5(char * target, char *key1, char *key2, char *key3) static int webSocketsEncodeHixie(rfbClientPtr cl, const char *src, int len, char **dst) { - int i, sz = 0; - unsigned char chr; + int sz = 0; ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; wsctx->encodeBuf[sz++] = '\x00'; - if (wsctx->base64) { - len = __b64_ntop((unsigned char *)src, len, wsctx->encodeBuf+sz, sizeof(wsctx->encodeBuf) - (sz + 1)); - if (len < 0) { - return len; - } - sz += len; - } else { - for (i=0; i < len; i++) { - chr = src[i]; - if (chr < 128) { - if (chr == 0x00) { - wsctx->encodeBuf[sz++] = '\xc4'; - wsctx->encodeBuf[sz++] = '\x80'; - } else { - wsctx->encodeBuf[sz++] = chr; - } - } else { - if (chr < 192) { - wsctx->encodeBuf[sz++] = '\xc2'; - wsctx->encodeBuf[sz++] = chr; - } else { - wsctx->encodeBuf[sz++] = '\xc3'; - wsctx->encodeBuf[sz++] = chr - 64; - } - } - } + len = __b64_ntop((unsigned char *)src, len, wsctx->encodeBuf+sz, sizeof(wsctx->encodeBuf) - (sz + 1)); + if (len < 0) { + return len; } + sz += len; + wsctx->encodeBuf[sz++] = '\xff'; *dst = wsctx->encodeBuf; return sz; @@ -524,9 +533,8 @@ ws_peek(rfbClientPtr cl, char *buf, int len) static int webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len) { - int retlen = 0, n, i, avail, modlen, needlen, actual; + int retlen = 0, n, i, avail, modlen, needlen; char *buf, *end = NULL; - unsigned char chr, chr2; ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; buf = wsctx->decodeBuf; @@ -539,133 +547,75 @@ webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len) } - if (wsctx->base64) { - /* Base64 encoded WebSockets stream */ + /* Base64 encoded WebSockets stream */ - if (buf[0] == '\xff') { - i = ws_read(cl, buf, 1); /* Consume marker */ - buf++; - n--; - } - if (n == 0) { - errno = EAGAIN; - return -1; - } - if (buf[0] == '\x00') { - i = ws_read(cl, buf, 1); /* Consume marker */ - buf++; - n--; - } - if (n == 0) { - errno = EAGAIN; - return -1; - } - - /* end = memchr(buf, '\xff', len*2+2); */ - end = memchr(buf, '\xff', n); - if (!end) { - end = buf + n; - } - avail = end - buf; - - len -= wsctx->carrylen; - - /* Determine how much base64 data we need */ - modlen = len + (len+2)/3; - needlen = modlen; - if (needlen % 4) { - needlen += 4 - (needlen % 4); - } + if (buf[0] == '\xff') { + i = ws_read(cl, buf, 1); /* Consume marker */ + buf++; + n--; + } + if (n == 0) { + errno = EAGAIN; + return -1; + } + if (buf[0] == '\x00') { + i = ws_read(cl, buf, 1); /* Consume marker */ + buf++; + n--; + } + if (n == 0) { + errno = EAGAIN; + return -1; + } - if (needlen > avail) { - /* rfbLog("Waiting for more base64 data\n"); */ - errno = EAGAIN; - return -1; - } + /* end = memchr(buf, '\xff', len*2+2); */ + end = memchr(buf, '\xff', n); + if (!end) { + end = buf + n; + } + avail = end - buf; - /* Any carryover from previous decode */ - for (i=0; i < wsctx->carrylen; i++) { - /* rfbLog("Adding carryover %d\n", wsctx->carryBuf[i]); */ - dst[i] = wsctx->carryBuf[i]; - retlen += 1; - } + len -= wsctx->carrylen; - /* Decode the rest of what we need */ - buf[needlen] = '\x00'; /* Replace end marker with end of string */ - /* rfbLog("buf: %s\n", buf); */ - n = __b64_pton(buf, (unsigned char *)dst+retlen, 2+len); - if (n < len) { - rfbErr("Base64 decode error\n"); - errno = EIO; - return -1; - } - retlen += n; + /* Determine how much base64 data we need */ + modlen = len + (len+2)/3; + needlen = modlen; + if (needlen % 4) { + needlen += 4 - (needlen % 4); + } - /* Consume the data from socket */ - i = ws_read(cl, buf, needlen); + if (needlen > avail) { + /* rfbLog("Waiting for more base64 data\n"); */ + errno = EAGAIN; + return -1; + } - wsctx->carrylen = n - len; - retlen -= wsctx->carrylen; - for (i=0; i < wsctx->carrylen; i++) { - /* rfbLog("Saving carryover %d\n", dst[retlen + i]); */ - wsctx->carryBuf[i] = dst[retlen + i]; - } - } else { - /* UTF-8 encoded WebSockets stream */ - - actual = 0; - for (needlen = 0; needlen < n && actual < len; needlen++) { - chr = buf[needlen]; - if ((chr > 0) && (chr < 128)) { - actual++; - } else if ((chr > 127) && (chr < 255)) { - if (needlen + 1 >= n) { - break; - } - needlen++; - actual++; - } - } + /* Any carryover from previous decode */ + for (i=0; i < wsctx->carrylen; i++) { + /* rfbLog("Adding carryover %d\n", wsctx->carryBuf[i]); */ + dst[i] = wsctx->carryBuf[i]; + retlen += 1; + } - if (actual < len) { - errno = EAGAIN; - return -1; - } + /* Decode the rest of what we need */ + buf[needlen] = '\x00'; /* Replace end marker with end of string */ + /* rfbLog("buf: %s\n", buf); */ + n = __b64_pton(buf, (unsigned char *)dst+retlen, 2+len); + if (n < len) { + rfbErr("Base64 decode error\n"); + errno = EIO; + return -1; + } + retlen += n; - /* Consume what we need */ - if ((n = ws_read(cl, buf, needlen)) < needlen) { - return n; - } + /* Consume the data from socket */ + i = ws_read(cl, buf, needlen); - while (retlen < len) { - chr = buf[0]; - buf += 1; - if (chr == 0) { - /* Begin frame marker, just skip it */ - } else if (chr == 255) { - /* Begin frame marker, just skip it */ - } else if (chr < 128) { - dst[retlen++] = chr; - } else { - chr2 = buf[0]; - buf += 1; - switch (chr) { - case (unsigned char) '\xc2': - dst[retlen++] = chr2; - break; - case (unsigned char) '\xc3': - dst[retlen++] = chr2 + 64; - break; - case (unsigned char) '\xc4': - dst[retlen++] = 0; - break; - default: - rfbErr("Invalid UTF-8 encoding\n"); - errno = EIO; - return -1; - } - } - } + wsctx->carrylen = n - len; + retlen -= wsctx->carrylen; + for (i=0; i < wsctx->carrylen; i++) { + /* rfbLog("Saving carryover %d\n", dst[retlen + i]); */ + wsctx->carryBuf[i] = dst[retlen + i]; } /* rfbLog("<< webSocketsDecode, retlen: %d\n", retlen); */ @@ -675,7 +625,7 @@ webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len) static int webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len) { - char *buf, *payload, *rbuf; + char *buf, *payload; int ret = -1, result = -1; int total = 0; ws_mask_t mask; @@ -683,7 +633,7 @@ webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len) int i, j; unsigned char opcode; ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; - int flength, fin, fhlen, blen; + int flength, fin, fhlen; // rfbLog(" <== %s[%d]: %d cl: %p, wsctx: %p-%p (%d)\n", __func__, gettid(), len, cl, wsctx, (char *)wsctx + sizeof(ws_ctx_t), sizeof(ws_ctx_t)); -- cgit v1.2.1 From 0f2ac00f6ee8102c3695f40a1896edff7c54fbd5 Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Mon, 29 Aug 2011 08:53:41 +0200 Subject: websockets: use 32bit Xor in webSocketsDecodeHybi() --- libvncserver/websockets.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'libvncserver') diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index 0cce3c9..cec2230 100755 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -626,11 +626,12 @@ static int webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len) { char *buf, *payload; + uint32_t *payload32; int ret = -1, result = -1; int total = 0; ws_mask_t mask; ws_header_t *header; - int i, j; + int i; unsigned char opcode; ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; int flength, fin, fhlen; @@ -714,10 +715,14 @@ webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len) buf[ret] = '\0'; } - /* process 1 frame */ - for (i = 0; i < flength; i++) { - j = i % 4; - payload[i] ^= mask.c[j]; + /* process 1 frame (32 bit op) */ + payload32 = (uint32_t *)payload; + for (i = 0; i < flength / 4; i++) { + payload32[i] ^= mask.u; + } + /* process the remaining bytes (if any) */ + for (i*=4; i < flength; i++) { + payload[i] ^= mask.c[i % 4]; } switch (opcode) { -- cgit v1.2.1 From b1671e6de99f00a5391bcb08f7106462b97b567a Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Tue, 30 Aug 2011 12:17:20 +0200 Subject: websockets: fix webSocketCheckDisconnect() Do not consume the peeked data if no close frame was detected. --- libvncserver/websockets.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libvncserver') diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index cec2230..777c3d8 100755 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -876,7 +876,7 @@ webSocketCheckDisconnect(rfbClientPtr cl) doclose = 1; break; default: - ; + return FALSE; } if (cl->sslctx) -- cgit v1.2.1 From 7d1c1033c5ee8689fd7896b2d7fadf8bd552382b Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Tue, 30 Aug 2011 12:25:53 +0200 Subject: rfbssl_gnutls: Merge rfbssl_peek/rfbssl_read into one function --- libvncserver/rfbssl_gnutls.c | 88 ++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 53 deletions(-) (limited to 'libvncserver') diff --git a/libvncserver/rfbssl_gnutls.c b/libvncserver/rfbssl_gnutls.c index 09cc89e..66f4bc8 100644 --- a/libvncserver/rfbssl_gnutls.c +++ b/libvncserver/rfbssl_gnutls.c @@ -190,11 +190,8 @@ int rfbssl_write(rfbClientPtr cl, const char *buf, int bufsize) return ret; } -int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize) +static void rfbssl_gc_peekbuf(struct rfbssl_ctx *ctx, int bufsize) { - int ret = -1; - struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; - if (ctx->peekstart) { int spaceleft = sizeof(ctx->peekbuf) - ctx->peeklen - ctx->peekstart; if (spaceleft < bufsize) { @@ -202,44 +199,40 @@ int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize) ctx->peekstart = 0; } } +} + +static int __rfbssl_read(rfbClientPtr cl, char *buf, int bufsize, int peek) +{ + int ret = 0; + struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; + + rfbssl_gc_peekbuf(ctx, bufsize); - /* If we have any peek data, simply return that. */ if (ctx->peeklen) { - if (bufsize > ctx->peeklen) { - /* more than we have, so we are trying to read the remaining - * bytes - **/ - int required = bufsize - ctx->peeklen; - int total = ctx->peekstart + ctx->peeklen; - int n, avail = sizeof(ctx->peekbuf) - total; - - if (required > avail) - required = avail; - - if (!required) { - rfbErr("%s: no space left\n", __func__); - } else if ((n = rfbssl_do_read(cl, ctx->peekbuf + total, required)) < 0) { - rfbErr("%s: read error\n", __func__); - return n; - } else { - ctx->peeklen += n; - } - ret = ctx->peeklen; - } else { - /* simply return what we have */ - ret = bufsize; + /* If we have any peek data, simply return that. */ + ret = bufsize < ctx->peeklen ? bufsize : ctx->peeklen; + memcpy (buf, ctx->peekbuf + ctx->peekstart, ret); + if (!peek) { + ctx->peeklen -= ret; + if (ctx->peeklen != 0) + ctx->peekstart += ret; + else + ctx->peekstart = 0; } - } else { - ret = bufsize; - if (ret > sizeof(ctx->peekbuf)) - ret = sizeof(ctx->peekbuf); - - if ((ret = rfbssl_do_read(cl, ctx->peekbuf, ret)) > 0) - ctx->peeklen = ret; } - if (ret >= 0) { - memcpy(buf, ctx->peekbuf + ctx->peekstart, ret); + if (ret < bufsize) { + int n; + /* read the remaining data */ + if ((n = rfbssl_do_read(cl, buf + ret, bufsize - ret)) <= 0) { + rfbErr("rfbssl_%s: %s error\n", __func__, peek ? "peek" : "read"); + return n; + } + if (peek) { + memcpy(ctx->peekbuf + ctx->peekstart + ctx->peeklen, buf + ret, n); + ctx->peeklen += n; + } + ret += n; } return ret; @@ -247,23 +240,12 @@ int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize) int rfbssl_read(rfbClientPtr cl, char *buf, int bufsize) { - int ret; - struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; - - if (ctx->peeklen) { - /* If we have any peek data, simply return that. */ - ret = bufsize < ctx->peeklen ? bufsize : ctx->peeklen; - memcpy (buf, ctx->peekbuf + ctx->peekstart, ret); - ctx->peeklen -= ret; - if (ctx->peeklen != 0) - ctx->peekstart += ret; - else - ctx->peekstart = 0; - } else { - ret = rfbssl_do_read(cl, buf, bufsize); - } + return __rfbssl_read(cl, buf, bufsize, 0); +} - return ret; +int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize) +{ + return __rfbssl_read(cl, buf, bufsize, 1); } int rfbssl_pending(rfbClientPtr cl) -- cgit v1.2.1