From a01a18df1d44ec875708d5fc9d6c4e0934535974 Mon Sep 17 00:00:00 2001 From: Balazs Ludmany Date: Tue, 21 Jun 2016 10:03:52 +0200 Subject: Add function pointers for every type of rectangle --- libvncclient/corre.c | 4 +- libvncclient/hextile.c | 8 ++-- libvncclient/rfbproto.c | 106 ++--------------------------------------------- libvncclient/rre.c | 4 +- libvncclient/tight.c | 11 +++-- libvncclient/ultra.c | 4 +- libvncclient/vncviewer.c | 98 +++++++++++++++++++++++++++++++++++++++++++ libvncclient/zlib.c | 2 +- libvncclient/zrle.c | 4 +- 9 files changed, 121 insertions(+), 120 deletions(-) (limited to 'libvncclient') diff --git a/libvncclient/corre.c b/libvncclient/corre.c index baf91cc..66e3b08 100644 --- a/libvncclient/corre.c +++ b/libvncclient/corre.c @@ -46,7 +46,7 @@ HandleCoRREBPP (rfbClient* client, int rx, int ry, int rw, int rh) if (!ReadFromRFBServer(client, (char *)&pix, sizeof(pix))) return FALSE; - FillRectangle(client, rx, ry, rw, rh, pix); + client->GotFillRect(client, rx, ry, rw, rh, pix); if (!ReadFromRFBServer(client, client->buffer, hdr.nSubrects * (4 + (BPP / 8)))) return FALSE; @@ -61,7 +61,7 @@ HandleCoRREBPP (rfbClient* client, int rx, int ry, int rw, int rh) w = *ptr++; h = *ptr++; - FillRectangle(client, rx+x, ry+y, w, h, pix); + client->GotFillRect(client, rx+x, ry+y, w, h, pix); } return TRUE; diff --git a/libvncclient/hextile.c b/libvncclient/hextile.c index 8698445..05a7cf5 100644 --- a/libvncclient/hextile.c +++ b/libvncclient/hextile.c @@ -55,7 +55,7 @@ HandleHextileBPP (rfbClient* client, int rx, int ry, int rw, int rh) if (!ReadFromRFBServer(client, client->buffer, w * h * (BPP / 8))) return FALSE; - CopyRectangle(client, (uint8_t *)client->buffer, x, y, w, h); + client->GotBitmap(client, (uint8_t *)client->buffer, x, y, w, h); continue; } @@ -64,7 +64,7 @@ HandleHextileBPP (rfbClient* client, int rx, int ry, int rw, int rh) if (!ReadFromRFBServer(client, (char *)&bg, sizeof(bg))) return FALSE; - FillRectangle(client, x, y, w, h, bg); + client->GotFillRect(client, x, y, w, h, bg); if (subencoding & rfbHextileForegroundSpecified) if (!ReadFromRFBServer(client, (char *)&fg, sizeof(fg))) @@ -100,7 +100,7 @@ HandleHextileBPP (rfbClient* client, int rx, int ry, int rw, int rh) sh = rfbHextileExtractH(*ptr); ptr++; - FillRectangle(client, x+sx, y+sy, sw, sh, fg); + client->GotFillRect(client, x+sx, y+sy, sw, sh, fg); } } else { @@ -115,7 +115,7 @@ HandleHextileBPP (rfbClient* client, int rx, int ry, int rw, int rh) sh = rfbHextileExtractH(*ptr); ptr++; - FillRectangle(client, x+sx, y+sy, sw, sh, fg); + client->GotFillRect(client, x+sx, y+sy, sw, sh, fg); } } } diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index 94b9bdb..e099f1a 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -145,101 +145,6 @@ void* rfbClientGetClientData(rfbClient* client, void* tag) return NULL; } -/* messages */ - -static void FillRectangle(rfbClient* client, int x, int y, int w, int h, uint32_t colour) { - int i,j; - - if (client->frameBuffer == NULL) { - return; - } - -#define FILL_RECT(BPP) \ - for(j=y*client->width;j<(y+h)*client->width;j+=client->width) \ - for(i=x;iframeBuffer)[j+i]=colour; - - switch(client->format.bitsPerPixel) { - case 8: FILL_RECT(8); break; - case 16: FILL_RECT(16); break; - case 32: FILL_RECT(32); break; - default: - rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel); - } -} - -static void CopyRectangle(rfbClient* client, uint8_t* buffer, int x, int y, int w, int h) { - int j; - - if (client->frameBuffer == NULL) { - return; - } - -#define COPY_RECT(BPP) \ - { \ - int rs = w * BPP / 8, rs2 = client->width * BPP / 8; \ - for (j = ((x * (BPP / 8)) + (y * rs2)); j < (y + h) * rs2; j += rs2) { \ - memcpy(client->frameBuffer + j, buffer, rs); \ - buffer += rs; \ - } \ - } - - switch(client->format.bitsPerPixel) { - case 8: COPY_RECT(8); break; - case 16: COPY_RECT(16); break; - case 32: COPY_RECT(32); break; - default: - rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel); - } -} - -/* TODO: test */ -static void CopyRectangleFromRectangle(rfbClient* client, int src_x, int src_y, int w, int h, int dest_x, int dest_y) { - int i,j; - - if (client->frameBuffer == NULL) { - return; - } - -#define COPY_RECT_FROM_RECT(BPP) \ - { \ - uint##BPP##_t* _buffer=((uint##BPP##_t*)client->frameBuffer)+(src_y-dest_y)*client->width+src_x-dest_x; \ - if (dest_y < src_y) { \ - for(j = dest_y*client->width; j < (dest_y+h)*client->width; j += client->width) { \ - if (dest_x < src_x) { \ - for(i = dest_x; i < dest_x+w; i++) { \ - ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \ - } \ - } else { \ - for(i = dest_x+w-1; i >= dest_x; i--) { \ - ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \ - } \ - } \ - } \ - } else { \ - for(j = (dest_y+h-1)*client->width; j >= dest_y*client->width; j-=client->width) { \ - if (dest_x < src_x) { \ - for(i = dest_x; i < dest_x+w; i++) { \ - ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \ - } \ - } else { \ - for(i = dest_x+w-1; i >= dest_x; i--) { \ - ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \ - } \ - } \ - } \ - } \ - } - - switch(client->format.bitsPerPixel) { - case 8: COPY_RECT_FROM_RECT(8); break; - case 16: COPY_RECT_FROM_RECT(16); break; - case 32: COPY_RECT_FROM_RECT(32); break; - default: - rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel); - } -} - static rfbBool HandleRRE8(rfbClient* client, int rx, int ry, int rw, int rh); static rfbBool HandleRRE16(rfbClient* client, int rx, int ry, int rw, int rh); static rfbBool HandleRRE32(rfbClient* client, int rx, int ry, int rw, int rh); @@ -1956,7 +1861,7 @@ HandleRFBServerMessage(rfbClient* client) if (!ReadFromRFBServer(client, client->buffer,bytesPerLine * linesToRead)) return FALSE; - CopyRectangle(client, (uint8_t *)client->buffer, + client->GotBitmap(client, (uint8_t *)client->buffer, rect.r.x, y, rect.r.w,linesToRead); h -= linesToRead; @@ -1982,13 +1887,8 @@ HandleRFBServerMessage(rfbClient* client) client->SoftCursorLockArea(client, cr.srcX, cr.srcY, rect.r.w, rect.r.h); - if (client->GotCopyRect != NULL) { - client->GotCopyRect(client, cr.srcX, cr.srcY, rect.r.w, rect.r.h, - rect.r.x, rect.r.y); - } else - CopyRectangleFromRectangle(client, - cr.srcX, cr.srcY, rect.r.w, rect.r.h, - rect.r.x, rect.r.y); + client->GotCopyRect(client, cr.srcX, cr.srcY, rect.r.w, rect.r.h, + rect.r.x, rect.r.y); break; } diff --git a/libvncclient/rre.c b/libvncclient/rre.c index 94158c9..752d7cc 100644 --- a/libvncclient/rre.c +++ b/libvncclient/rre.c @@ -45,7 +45,7 @@ HandleRREBPP (rfbClient* client, int rx, int ry, int rw, int rh) if (!ReadFromRFBServer(client, (char *)&pix, sizeof(pix))) return FALSE; - FillRectangle(client, rx, ry, rw, rh, pix); + client->GotFillRect(client, rx, ry, rw, rh, pix); for (i = 0; i < hdr.nSubrects; i++) { if (!ReadFromRFBServer(client, (char *)&pix, sizeof(pix))) @@ -59,7 +59,7 @@ HandleRREBPP (rfbClient* client, int rx, int ry, int rw, int rh) subrect.w = rfbClientSwap16IfLE(subrect.w); subrect.h = rfbClientSwap16IfLE(subrect.h); - FillRectangle(client, rx+subrect.x, ry+subrect.y, subrect.w, subrect.h, pix); + client->GotFillRect(client, rx+subrect.x, ry+subrect.y, subrect.w, subrect.h, pix); } return TRUE; diff --git a/libvncclient/tight.c b/libvncclient/tight.c index 2f9fbab..2447ad8 100644 --- a/libvncclient/tight.c +++ b/libvncclient/tight.c @@ -131,7 +131,7 @@ HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh) return FALSE; #endif - FillRectangle(client, rx, ry, rw, rh, fill_colour); + client->GotFillRect(client, rx, ry, rw, rh, fill_colour); return TRUE; } @@ -198,7 +198,7 @@ HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh) buffer2 = &client->buffer[TIGHT_MIN_TO_COMPRESS * 4]; filterFn(client, rh, (CARDBPP *)buffer2); - CopyRectangle(client, (uint8_t *)buffer2, rx, ry, rw, rh); + client->GotBitmap(client, (uint8_t *)buffer2, rx, ry, rw, rh); return TRUE; } @@ -277,7 +277,7 @@ HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh) if (extraBytes > 0) memcpy(client->buffer, &client->buffer[numRows * rowSize], extraBytes); - CopyRectangle(client, (uint8_t *)buffer2, rx, ry+rowsProcessed, rw, numRows); + client->GotBitmap(client, (uint8_t *)buffer2, rx, ry+rowsProcessed, rw, numRows); rowsProcessed += numRows; } @@ -547,6 +547,9 @@ DecompressJpegRectBPP(rfbClient* client, int x, int y, int w, int h) return FALSE; } + if(client->GotJpeg != NULL) + return client->GotJpeg(client, compressedData, compressedLen, x, y, w, h); + cinfo.err = jpeg_std_error(&jerr); cinfo.client_data = client; jpeg_create_decompress(&cinfo); @@ -577,7 +580,7 @@ DecompressJpegRectBPP(rfbClient* client, int x, int y, int w, int h) *pixelPtr++ = RGB24_TO_PIXEL(BPP, client->buffer[dx*3], client->buffer[dx*3+1], client->buffer[dx*3+2]); } - CopyRectangle(client, (uint8_t *)&client->buffer[RFB_BUFFER_SIZE / 2], x, y + dy, w, 1); + client->GotBitmap(client, (uint8_t *)&client->buffer[RFB_BUFFER_SIZE / 2], x, y + dy, w, 1); dy++; } diff --git a/libvncclient/ultra.c b/libvncclient/ultra.c index dac89b5..d816368 100644 --- a/libvncclient/ultra.c +++ b/libvncclient/ultra.c @@ -98,7 +98,7 @@ HandleUltraBPP (rfbClient* client, int rx, int ry, int rw, int rh) /* Put the uncompressed contents of the update on the screen. */ if ( inflateResult == LZO_E_OK ) { - CopyRectangle(client, (unsigned char *)client->raw_buffer, rx, ry, rw, rh); + client->GotBitmap(client, (unsigned char *)client->raw_buffer, rx, ry, rw, rh); } else { @@ -199,7 +199,7 @@ HandleUltraZipBPP (rfbClient* client, int rx, int ry, int rw, int rh) if (se == rfbEncodingRaw) { - CopyRectangle(client, (unsigned char *)ptr, sx, sy, sw, sh); + client->GotBitmap(client, (unsigned char *)ptr, sx, sy, sw, sh); ptr += ((sw * sh) * (BPP / 8)); } } diff --git a/libvncclient/vncviewer.c b/libvncclient/vncviewer.c index d81e298..2028bcd 100644 --- a/libvncclient/vncviewer.c +++ b/libvncclient/vncviewer.c @@ -113,6 +113,101 @@ static rfbBool MallocFrameBuffer(rfbClient* client) { return client->frameBuffer?TRUE:FALSE; } +/* messages */ + +static void FillRectangle(rfbClient* client, int x, int y, int w, int h, uint32_t colour) { + int i,j; + + if (client->frameBuffer == NULL) { + return; + } + +#define FILL_RECT(BPP) \ + for(j=y*client->width;j<(y+h)*client->width;j+=client->width) \ + for(i=x;iframeBuffer)[j+i]=colour; + + switch(client->format.bitsPerPixel) { + case 8: FILL_RECT(8); break; + case 16: FILL_RECT(16); break; + case 32: FILL_RECT(32); break; + default: + rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel); + } +} + +static void CopyRectangle(rfbClient* client, uint8_t* buffer, int x, int y, int w, int h) { + int j; + + if (client->frameBuffer == NULL) { + return; + } + +#define COPY_RECT(BPP) \ + { \ + int rs = w * BPP / 8, rs2 = client->width * BPP / 8; \ + for (j = ((x * (BPP / 8)) + (y * rs2)); j < (y + h) * rs2; j += rs2) { \ + memcpy(client->frameBuffer + j, buffer, rs); \ + buffer += rs; \ + } \ + } + + switch(client->format.bitsPerPixel) { + case 8: COPY_RECT(8); break; + case 16: COPY_RECT(16); break; + case 32: COPY_RECT(32); break; + default: + rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel); + } +} + +/* TODO: test */ +static void CopyRectangleFromRectangle(rfbClient* client, int src_x, int src_y, int w, int h, int dest_x, int dest_y) { + int i,j; + + if (client->frameBuffer == NULL) { + return; + } + +#define COPY_RECT_FROM_RECT(BPP) \ + { \ + uint##BPP##_t* _buffer=((uint##BPP##_t*)client->frameBuffer)+(src_y-dest_y)*client->width+src_x-dest_x; \ + if (dest_y < src_y) { \ + for(j = dest_y*client->width; j < (dest_y+h)*client->width; j += client->width) { \ + if (dest_x < src_x) { \ + for(i = dest_x; i < dest_x+w; i++) { \ + ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \ + } \ + } else { \ + for(i = dest_x+w-1; i >= dest_x; i--) { \ + ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \ + } \ + } \ + } \ + } else { \ + for(j = (dest_y+h-1)*client->width; j >= dest_y*client->width; j-=client->width) { \ + if (dest_x < src_x) { \ + for(i = dest_x; i < dest_x+w; i++) { \ + ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \ + } \ + } else { \ + for(i = dest_x+w-1; i >= dest_x; i--) { \ + ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \ + } \ + } \ + } \ + } \ + } + + switch(client->format.bitsPerPixel) { + case 8: COPY_RECT_FROM_RECT(8); break; + case 16: COPY_RECT_FROM_RECT(16); break; + case 32: COPY_RECT_FROM_RECT(32); break; + default: + rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel); + } +} + static void initAppData(AppData* data) { data->shareDesktop=TRUE; data->viewOnly=FALSE; @@ -208,6 +303,9 @@ rfbClient* rfbGetClient(int bitsPerSample,int samplesPerPixel, client->SoftCursorLockArea = DummyRect; client->SoftCursorUnlockScreen = Dummy; client->GotFrameBufferUpdate = DummyRect; + client->GotCopyRect = CopyRectangleFromRectangle; + client->GotFillRect = FillRectangle; + client->GotBitmap = CopyRectangle; client->FinishedFrameBufferUpdate = NULL; client->GetPassword = ReadPassword; client->MallocFrameBuffer = MallocFrameBuffer; diff --git a/libvncclient/zlib.c b/libvncclient/zlib.c index e872d40..fc6f138 100644 --- a/libvncclient/zlib.c +++ b/libvncclient/zlib.c @@ -142,7 +142,7 @@ HandleZlibBPP (rfbClient* client, int rx, int ry, int rw, int rh) if ( inflateResult == Z_OK ) { /* Put the uncompressed contents of the update on the screen. */ - CopyRectangle(client, (uint8_t *)client->raw_buffer, rx, ry, rw, rh); + client->GotBitmap(client, (uint8_t *)client->raw_buffer, rx, ry, rw, rh); } else { diff --git a/libvncclient/zrle.c b/libvncclient/zrle.c index 0128146..e732046 100644 --- a/libvncclient/zrle.c +++ b/libvncclient/zrle.c @@ -278,7 +278,7 @@ static int HandleZRLETile(rfbClient* client, for(i=x; iframeBuffer)[j+i] = UncompressCPixel(buffer); #else - CopyRectangle(client, buffer, x, y, w, h); + client->GotBitmap(client, buffer, x, y, w, h); buffer+=w*h*REALBPP/8; #endif } @@ -289,7 +289,7 @@ static int HandleZRLETile(rfbClient* client, if(1+REALBPP/8>buffer_length) return -4; - FillRectangle(client, x, y, w, h, color); + client->GotFillRect(client, x, y, w, h, color); buffer+=REALBPP/8; -- cgit v1.2.1 From dede3aea22640d9b036f358b228aa611da6bc0d9 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sat, 28 Jan 2017 17:59:52 +0100 Subject: Fix LibVNCClient compilation with MSVC 2014 --- libvncclient/listen.c | 4 ++++ libvncclient/sockets.c | 2 ++ 2 files changed, 6 insertions(+) (limited to 'libvncclient') diff --git a/libvncclient/listen.c b/libvncclient/listen.c index e989d6a..8674b3f 100644 --- a/libvncclient/listen.c +++ b/libvncclient/listen.c @@ -25,7 +25,9 @@ #ifdef __STRICT_ANSI__ #define _BSD_SOURCE #endif +#if LIBVNCSERVER_HAVE_UNISTD_H #include +#endif #include #ifdef WIN32 #define close closesocket @@ -34,7 +36,9 @@ #include #include #endif +#if LIBVNCSERVER_HAVE_SYS_TIME_H #include +#endif #include /* diff --git a/libvncclient/sockets.c b/libvncclient/sockets.c index 8ddfd9d..1019580 100644 --- a/libvncclient/sockets.c +++ b/libvncclient/sockets.c @@ -30,7 +30,9 @@ # define _POSIX_SOURCE #endif #endif +#if LIBVNCSERVER_HAVE_UNISTD_H #include +#endif #include #include #include -- cgit v1.2.1 From cca9892deba0db3a78dd8cfcf441f5b12c86adb7 Mon Sep 17 00:00:00 2001 From: Bert van Hall Date: Tue, 31 Jan 2017 14:42:45 +0100 Subject: libvncclient/tls_openssl: support openssl 1.1.x Treat openSSL data structures as opaque to achieve compatibility with openSSL 1.1.x. While at it, fix order of cleaning up in open_ssl_connection(). Signed-off-by: Bert van Hall --- libvncclient/tls_openssl.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'libvncclient') diff --git a/libvncclient/tls_openssl.c b/libvncclient/tls_openssl.c index a531778..1b6c986 100644 --- a/libvncclient/tls_openssl.c +++ b/libvncclient/tls_openssl.c @@ -189,7 +189,7 @@ ssl_verify (int ok, X509_STORE_CTX *ctx) ssl = X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx ()); - client = SSL_CTX_get_app_data (ssl->ctx); + client = SSL_CTX_get_app_data (SSL_get_SSL_CTX(ssl)); cert = X509_STORE_CTX_get_current_cert (ctx); err = X509_STORE_CTX_get_error (ctx); @@ -287,11 +287,10 @@ open_ssl_connection (rfbClient *client, int sockfd, rfbBool anonTLS) { if (wait_for_data(ssl, n, 1) != 1) { - finished = 1; - if (ssl->ctx) - SSL_CTX_free (ssl->ctx); + finished = 1; + SSL_shutdown(ssl); SSL_free(ssl); - SSL_shutdown (ssl); + SSL_CTX_free(ssl_ctx); return NULL; } -- cgit v1.2.1 From fe943395873c6cbfdfa0eadb8a5d13f2c95e8614 Mon Sep 17 00:00:00 2001 From: Bert van Hall Date: Wed, 8 Feb 2017 17:53:58 +0100 Subject: drop autotools Since autotools officially is no longer supported (see various github issues), drop the related infrastructure to stop tempting people to use it for building. Signed-off-by: Bert van Hall --- libvncclient/Makefile.am | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 libvncclient/Makefile.am (limited to 'libvncclient') diff --git a/libvncclient/Makefile.am b/libvncclient/Makefile.am deleted file mode 100644 index bc2420b..0000000 --- a/libvncclient/Makefile.am +++ /dev/null @@ -1,29 +0,0 @@ -AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/common - -if HAVE_GNUTLS -TLSSRCS = tls_gnutls.c -TLSLIBS = @GNUTLS_LIBS@ -else -if HAVE_LIBSSL -TLSSRCS = tls_openssl.c -TLSLIBS = @SSL_LIBS@ @CRYPT_LIBS@ -else -TLSSRCS = tls_none.c -endif -endif - - -libvncclient_la_SOURCES=cursor.c listen.c rfbproto.c sockets.c vncviewer.c ../common/minilzo.c $(TLSSRCS) -libvncclient_la_LIBADD=$(TLSLIBS) - -noinst_HEADERS=../common/lzodefs.h ../common/lzoconf.h ../common/minilzo.h tls.h - -rfbproto.o: rfbproto.c corre.c hextile.c rre.c tight.c zlib.c zrle.c ultra.c - -EXTRA_DIST=corre.c hextile.c rre.c tight.c zlib.c zrle.c ultra.c tls_gnutls.c tls_openssl.c tls_none.c - -$(libvncclient_la_OBJECTS): ../rfb/rfbclient.h - -lib_LTLIBRARIES=libvncclient.la -libvncclient_la_LDFLAGS = -version-info 1:0:0 - -- cgit v1.2.1 From ec6d0f72592a77601f632890d79921b5ca5d90f6 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 26 Mar 2017 15:03:24 +0200 Subject: Fix a compiler warning --- libvncclient/vncviewer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libvncclient') diff --git a/libvncclient/vncviewer.c b/libvncclient/vncviewer.c index afd84fd..fb25023 100644 --- a/libvncclient/vncviewer.c +++ b/libvncclient/vncviewer.c @@ -145,7 +145,7 @@ static void FillRectangle(rfbClient* client, int x, int y, int w, int h, uint32_ } } -static void CopyRectangle(rfbClient* client, uint8_t* buffer, int x, int y, int w, int h) { +static void CopyRectangle(rfbClient* client, const uint8_t* buffer, int x, int y, int w, int h) { int j; if (client->frameBuffer == NULL) { -- cgit v1.2.1 From 80ad74f7613d7d790a5152ee377af361ecd3ff9b Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 26 Mar 2017 16:11:16 +0200 Subject: Fix building for Android and add build instructions to README --- libvncclient/listen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libvncclient') diff --git a/libvncclient/listen.c b/libvncclient/listen.c index 8674b3f..4ecedff 100644 --- a/libvncclient/listen.c +++ b/libvncclient/listen.c @@ -89,7 +89,7 @@ listenForIncomingConnections(rfbClient* client) int r; /* reap any zombies */ int status, pid; - while ((pid= wait3(&status, WNOHANG, (struct rusage *)0))>0); + while ((pid= wait4(-1, &status, WNOHANG, (struct rusage *)0))>0); /* TODO: callback for discard any events (like X11 events) */ -- cgit v1.2.1 From 652f5a4b1c72a0c9041ec4140d73e66a975206ae Mon Sep 17 00:00:00 2001 From: dborth Date: Mon, 3 Apr 2017 09:43:44 -0600 Subject: Set trueColour flag to 1 instead of 255 It turns out some server implementations (namely VMware ESXi 6.5) expect 1 as the only non-zero value for the SetPixelFormat message whereas the protocol states every non-zero value is valid (https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#setpixelformat). Anyway, setting this to 1 shouldn't hurt. Fixes #141 --- libvncclient/vncviewer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libvncclient') diff --git a/libvncclient/vncviewer.c b/libvncclient/vncviewer.c index fb25023..780a1cb 100644 --- a/libvncclient/vncviewer.c +++ b/libvncclient/vncviewer.c @@ -280,7 +280,7 @@ rfbClient* rfbGetClient(int bitsPerSample,int samplesPerPixel, client->format.depth = bitsPerSample*samplesPerPixel; client->appData.requestedDepth=client->format.depth; client->format.bigEndian = *(char *)&client->endianTest?FALSE:TRUE; - client->format.trueColour = TRUE; + client->format.trueColour = 1; if (client->format.bitsPerPixel == 8) { client->format.redMax = 7; -- cgit v1.2.1 From 6c312aaf5be2e1db3bb414d430ab9338d4efced6 Mon Sep 17 00:00:00 2001 From: simon Date: Thu, 20 Apr 2017 21:08:23 +0100 Subject: Added support for X509 server certificate verification as part of the handshake process. --- libvncclient/tls_gnutls.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'libvncclient') diff --git a/libvncclient/tls_gnutls.c b/libvncclient/tls_gnutls.c index b9ffe89..4a798f4 100644 --- a/libvncclient/tls_gnutls.c +++ b/libvncclient/tls_gnutls.c @@ -170,7 +170,7 @@ InitializeTLSSession(rfbClient* client, rfbBool anonTLS) static rfbBool SetTLSAnonCredential(rfbClient* client) { - gnutls_anon_client_credentials anonCred; + gnutls_anon_client_credentials_t anonCred; int ret; if ((ret = gnutls_anon_allocate_client_credentials(&anonCred)) < 0 || @@ -200,6 +200,21 @@ HandshakeTLS(rfbClient* client) continue; } rfbClientLog("TLS handshake failed: %s.\n", gnutls_strerror(ret)); + if (ret == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR) { + gnutls_datum_t out; + unsigned status; + int type; + + type = gnutls_certificate_type_get((gnutls_session_t)client->tlsSession); + status = gnutls_session_get_verify_cert_status((gnutls_session_t)client->tlsSession); + + if (gnutls_certificate_verification_status_print(status, type, &out, 0)) + rfbClientLog("Certificate verification failed but could not determine reason"); + else { + rfbClientLog("Certificate verification failed: %s\n", out.data); + gnutls_free(out.data); + } + } FreeTLS(client); return FALSE; } @@ -212,6 +227,11 @@ HandshakeTLS(rfbClient* client) } rfbClientLog("TLS handshake done.\n"); + char *desc; + desc = gnutls_session_get_desc((gnutls_session_t)client->tlsSession); + rfbClientLog("Session info: %s\n", desc); + gnutls_free(desc); + return TRUE; } @@ -455,12 +475,11 @@ HandleVeNCryptAuth(rfbClient* client) FreeTLS(client); return FALSE; } + gnutls_session_set_verify_cert((gnutls_session_t)client->tlsSession, client->serverHost, 0); } if (!HandshakeTLS(client)) return FALSE; - /* TODO: validate certificate */ - /* We are done here. The caller should continue with client->subAuthScheme * to do actual sub authentication. */ -- cgit v1.2.1 From cc69ee96e113c8b86f132f6316f22faa3e9205ce Mon Sep 17 00:00:00 2001 From: simon Date: Tue, 25 Apr 2017 00:45:05 +0100 Subject: Modified certificate verification for compatibility with GnuTLS 2.12.23 --- libvncclient/tls_gnutls.c | 124 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 102 insertions(+), 22 deletions(-) (limited to 'libvncclient') diff --git a/libvncclient/tls_gnutls.c b/libvncclient/tls_gnutls.c index 4a798f4..2fc65a5 100644 --- a/libvncclient/tls_gnutls.c +++ b/libvncclient/tls_gnutls.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #ifdef WIN32 @@ -39,6 +40,101 @@ static gnutls_dh_params_t rfbDHParams; static rfbBool rfbTLSInitialized = FALSE; +static int +verify_certificate_callback (gnutls_session_t session) +{ + unsigned int status; + const gnutls_datum_t *cert_list; + unsigned int cert_list_size; + int ret; + gnutls_x509_crt_t cert; + rfbClient *sptr; + char *hostname; + + sptr = (rfbClient *)gnutls_session_get_ptr(session); + if (!sptr) { + rfbClientLog("Failed to validate certificate - missing client data\n"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + hostname = sptr->serverHost; + if (!hostname) { + rfbClientLog("No server hostname found for client\n"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + /* This verification function uses the trusted CAs in the credentials + * structure. So you must have installed one or more CA certificates. + */ + ret = gnutls_certificate_verify_peers2 (session, &status); + if (ret < 0) + { + rfbClientLog ("Certificate validation call failed\n"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + if (status & GNUTLS_CERT_INVALID) + rfbClientLog("The certificate is not trusted.\n"); + + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) + rfbClientLog("The certificate hasn't got a known issuer.\n"); + + if (status & GNUTLS_CERT_REVOKED) + rfbClientLog("The certificate has been revoked.\n"); + + if (status & GNUTLS_CERT_EXPIRED) + rfbClientLog("The certificate has expired\n"); + + if (status & GNUTLS_CERT_NOT_ACTIVATED) + rfbClientLog("The certificate is not yet activated\n"); + + if (status) + return GNUTLS_E_CERTIFICATE_ERROR; + + /* Up to here the process is the same for X.509 certificates and + * OpenPGP keys. From now on X.509 certificates are assumed. This can + * be easily extended to work with openpgp keys as well. + */ + if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509) { + rfbClientLog("The certificate was not X509\n"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + if (gnutls_x509_crt_init (&cert) < 0) + { + rfbClientLog("Error initialising certificate structure\n"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + cert_list = gnutls_certificate_get_peers (session, &cert_list_size); + if (cert_list == NULL) + { + rfbClientLog("No certificate was found!\n"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + /* This is not a real world example, since we only check the first + * certificate in the given chain. + */ + if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) + { + rfbClientLog("Error parsing certificate\n"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + if (!gnutls_x509_crt_check_hostname (cert, hostname)) + { + rfbClientLog("The certificate's owner does not match hostname '%s'\n", + hostname); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + gnutls_x509_crt_deinit (cert); + + /* notify gnutls to continue handshake normally */ + return 0; +} + static rfbBool InitializeTLS(void) { @@ -52,7 +148,7 @@ InitializeTLS(void) rfbClientLog("Failed to initialized GnuTLS: %s.\n", gnutls_strerror(ret)); return FALSE; } - rfbClientLog("GnuTLS initialized.\n"); + rfbClientLog("GnuTLS version %s initialized.\n", gnutls_check_version(NULL)); rfbTLSInitialized = TRUE; return TRUE; } @@ -200,21 +296,7 @@ HandshakeTLS(rfbClient* client) continue; } rfbClientLog("TLS handshake failed: %s.\n", gnutls_strerror(ret)); - if (ret == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR) { - gnutls_datum_t out; - unsigned status; - int type; - - type = gnutls_certificate_type_get((gnutls_session_t)client->tlsSession); - status = gnutls_session_get_verify_cert_status((gnutls_session_t)client->tlsSession); - - if (gnutls_certificate_verification_status_print(status, type, &out, 0)) - rfbClientLog("Certificate verification failed but could not determine reason"); - else { - rfbClientLog("Certificate verification failed: %s\n", out.data); - gnutls_free(out.data); - } - } + FreeTLS(client); return FALSE; } @@ -227,11 +309,6 @@ HandshakeTLS(rfbClient* client) } rfbClientLog("TLS handshake done.\n"); - char *desc; - desc = gnutls_session_get_desc((gnutls_session_t)client->tlsSession); - rfbClientLog("Session info: %s\n", desc); - gnutls_free(desc); - return TRUE; } @@ -469,13 +546,16 @@ HandleVeNCryptAuth(rfbClient* client) } else { + /* Set the certificate verification callback. */ + gnutls_certificate_set_verify_function (x509_cred, verify_certificate_callback); + gnutls_session_set_ptr ((gnutls_session_t)client->tlsSession, (void *)client); + if ((ret = gnutls_credentials_set((gnutls_session_t)client->tlsSession, GNUTLS_CRD_CERTIFICATE, x509_cred)) < 0) { rfbClientLog("Cannot set x509 credential: %s.\n", gnutls_strerror(ret)); FreeTLS(client); return FALSE; } - gnutls_session_set_verify_cert((gnutls_session_t)client->tlsSession, client->serverHost, 0); } if (!HandshakeTLS(client)) return FALSE; -- cgit v1.2.1 From cc10eab7ebb6162c5621c458eb1037b9ec814800 Mon Sep 17 00:00:00 2001 From: simon Date: Tue, 25 Apr 2017 01:53:37 +0100 Subject: Removed comment left over from development --- libvncclient/tls_gnutls.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'libvncclient') diff --git a/libvncclient/tls_gnutls.c b/libvncclient/tls_gnutls.c index 2fc65a5..f49fa85 100644 --- a/libvncclient/tls_gnutls.c +++ b/libvncclient/tls_gnutls.c @@ -113,9 +113,6 @@ verify_certificate_callback (gnutls_session_t session) return GNUTLS_E_CERTIFICATE_ERROR; } - /* This is not a real world example, since we only check the first - * certificate in the given chain. - */ if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) { rfbClientLog("Error parsing certificate\n"); -- cgit v1.2.1 From 2c87a631fa4f72178876901c87c85d2889bd7b40 Mon Sep 17 00:00:00 2001 From: simon Date: Fri, 5 May 2017 00:37:25 +0100 Subject: X509 certificate verification for OpenSSL --- libvncclient/tls_openssl.c | 249 ++++++++++++++++++++++++++++----------------- 1 file changed, 157 insertions(+), 92 deletions(-) (limited to 'libvncclient') diff --git a/libvncclient/tls_openssl.c b/libvncclient/tls_openssl.c index 1b6c986..2bdf3eb 100644 --- a/libvncclient/tls_openssl.c +++ b/libvncclient/tls_openssl.c @@ -168,53 +168,11 @@ InitializeTLS(void) SSLeay_add_ssl_algorithms(); RAND_load_file("/dev/urandom", 1024); - rfbClientLog("OpenSSL initialized.\n"); + rfbClientLog("OpenSSL version %s initialized.\n", SSLeay_version(SSLEAY_VERSION)); rfbTLSInitialized = TRUE; return TRUE; } -static int -ssl_verify (int ok, X509_STORE_CTX *ctx) -{ - unsigned char md5sum[16], fingerprint[40], *f; - rfbClient *client; - int err, i; - unsigned int md5len; - //char buf[257]; - X509 *cert; - SSL *ssl; - - if (ok) - return TRUE; - - ssl = X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx ()); - - client = SSL_CTX_get_app_data (SSL_get_SSL_CTX(ssl)); - - cert = X509_STORE_CTX_get_current_cert (ctx); - err = X509_STORE_CTX_get_error (ctx); - - /* calculate the MD5 hash of the raw certificate */ - md5len = sizeof (md5sum); - X509_digest (cert, EVP_md5 (), md5sum, &md5len); - for (i = 0, f = fingerprint; i < 16; i++, f += 3) - sprintf ((char *) f, "%.2x%c", md5sum[i], i != 15 ? ':' : '\0'); - -#define GET_STRING(name) X509_NAME_oneline (name, buf, 256) - - /* TODO: Don't just ignore certificate checks - - fingerprint = key to check in db - - GET_STRING (X509_get_issuer_name (cert)); - GET_STRING (X509_get_subject_name (cert)); - cert->valid (bool: GOOD or BAD) */ - - ok = TRUE; - - return ok; -} - static int sock_read_ready(SSL *ssl, uint32_t ms) { int r = 0; @@ -251,8 +209,12 @@ static int wait_for_data(SSL *ssl, int ret, int timeout) } break; - default: + default: retval = 3; + long verify_res = SSL_get_verify_result(ssl); + if (verify_res != X509_V_OK) + rfbClientLog("Could not verify server certificate: %s.\n", + X509_verify_cert_error_string(verify_res)); break; } @@ -261,17 +223,131 @@ static int wait_for_data(SSL *ssl, int ret, int timeout) return retval; } +static rfbBool +load_crls_from_file(char *file, SSL_CTX *ssl_ctx) +{ + X509_STORE *st; + X509_CRL *crl; + int i; + int count = 0; + BIO *bio; + STACK_OF(X509_INFO) *xis = NULL; + X509_INFO *xi; + + st = SSL_CTX_get_cert_store(ssl_ctx); + + int rv = 0; + + bio = BIO_new_file(file, "r"); + if (bio == NULL) + return FALSE; + + xis = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); + BIO_free(bio); + + for (i = 0; i < sk_X509_INFO_num(xis); i++) + { + xi = sk_X509_INFO_value(xis, i); + if (xi->crl) + { + X509_STORE_add_crl(st, xi->crl); + xi->crl = NULL; + count++; + } + } + + sk_X509_INFO_pop_free(xis, X509_INFO_free); + + if (count > 0) + return TRUE; + else + return FALSE; +} + static SSL * -open_ssl_connection (rfbClient *client, int sockfd, rfbBool anonTLS) +open_ssl_connection (rfbClient *client, int sockfd, rfbBool anonTLS, rfbCredential *cred) { SSL_CTX *ssl_ctx = NULL; SSL *ssl = NULL; int n, finished = 0; + X509_VERIFY_PARAM *param; + uint8_t verify_crls = cred->x509Credential.x509CrlVerifyMode; - ssl_ctx = SSL_CTX_new (SSLv23_client_method ()); - SSL_CTX_set_default_verify_paths (ssl_ctx); - SSL_CTX_set_verify (ssl_ctx, SSL_VERIFY_NONE, &ssl_verify); - ssl = SSL_new (ssl_ctx); + if (!(ssl_ctx = SSL_CTX_new(SSLv23_client_method()))) + { + rfbClientLog("Could not create new SSL context.\n"); + return NULL; + } + + param = X509_VERIFY_PARAM_new(); + + /* Setup verification if not anonymous */ + if (!anonTLS) + { + if (cred->x509Credential.x509CACertFile) + { + if (!SSL_CTX_load_verify_locations(ssl_ctx, cred->x509Credential.x509CACertFile, NULL)) + { + rfbClientLog("Failed to load CA certificate from %s.\n", + cred->x509Credential.x509CACertFile); + goto error_free_ctx; + } + } else { + rfbClientLog("Using default paths for certificate verification.\n"); + SSL_CTX_set_default_verify_paths (ssl_ctx); + } + + if (cred->x509Credential.x509CACrlFile) + { + if (!load_crls_from_file(cred->x509Credential.x509CACrlFile, ssl_ctx)) + { + rfbClientLog("CRLs could not be loaded.\n"); + goto error_free_ctx; + } + if (verify_crls == rfbX509CrlVerifyNone) verify_crls = rfbX509CrlVerifyAll; + } + + if (cred->x509Credential.x509ClientCertFile && cred->x509Credential.x509ClientKeyFile) + { + if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cred->x509Credential.x509ClientCertFile) != 1) + { + rfbClientLog("Client certificate could not be loaded.\n"); + goto error_free_ctx; + } + + if (SSL_CTX_use_PrivateKey_file(ssl_ctx, cred->x509Credential.x509ClientKeyFile, + SSL_FILETYPE_PEM) != 1) + { + rfbClientLog("Client private key could not be loaded.\n"); + goto error_free_ctx; + } + + if (SSL_CTX_check_private_key(ssl_ctx) == 0) { + rfbClientLog("Client certificate and private key do not match.\n"); + goto error_free_ctx; + } + } + + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); + + if (verify_crls == rfbX509CrlVerifyClient) + X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK); + else if (verify_crls == rfbX509CrlVerifyAll) + X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); + + if(!X509_VERIFY_PARAM_set1_host(param, client->serverHost, strlen(client->serverHost))) + { + rfbClientLog("Could not set server name for verification.\n"); + goto error_free_ctx; + } + SSL_CTX_set1_param(ssl_ctx, param); + } + + if (!(ssl = SSL_new (ssl_ctx))) + { + rfbClientLog("Could not create a new SSL session.\n"); + goto error_free_ctx; + } /* TODO: finetune this list, take into account anonTLS bool */ SSL_set_cipher_list(ssl, "ALL"); @@ -289,24 +365,32 @@ open_ssl_connection (rfbClient *client, int sockfd, rfbBool anonTLS) { finished = 1; SSL_shutdown(ssl); - SSL_free(ssl); - SSL_CTX_free(ssl_ctx); - return NULL; + goto error_free_ssl; } } } while( n != 1 && finished != 1 ); + X509_VERIFY_PARAM_free(param); return ssl; + +error_free_ssl: + SSL_free(ssl); + +error_free_ctx: + X509_VERIFY_PARAM_free(param); + SSL_CTX_free(ssl_ctx); + + return NULL; } static rfbBool -InitializeTLSSession(rfbClient* client, rfbBool anonTLS) +InitializeTLSSession(rfbClient* client, rfbBool anonTLS, rfbCredential *cred) { if (client->tlsSession) return TRUE; - client->tlsSession = open_ssl_connection (client, client->sock, anonTLS); + client->tlsSession = open_ssl_connection (client, client->sock, anonTLS, cred); if (!client->tlsSession) return FALSE; @@ -316,13 +400,6 @@ InitializeTLSSession(rfbClient* client, rfbBool anonTLS) return TRUE; } -static rfbBool -SetTLSAnonCredential(rfbClient* client) -{ - rfbClientLog("TLS anonymous credential created.\n"); - return TRUE; -} - static rfbBool HandshakeTLS(rfbClient* client) { @@ -344,7 +421,8 @@ return TRUE; timeout--; continue; } - rfbClientLog("TLS handshake failed: -.\n"); + rfbClientLog("TLS handshake failed.\n"); + FreeTLS(client); return FALSE; } @@ -429,22 +507,31 @@ ReadVeNCryptSecurityType(rfbClient* client, uint32_t *result) rfbBool HandleAnonTLSAuth(rfbClient* client) { - if (!InitializeTLS() || !InitializeTLSSession(client, TRUE)) return FALSE; - - if (!SetTLSAnonCredential(client)) return FALSE; + if (!InitializeTLS() || !InitializeTLSSession(client, TRUE, NULL)) return FALSE; if (!HandshakeTLS(client)) return FALSE; return TRUE; } +static void +FreeX509Credential(rfbCredential *cred) +{ + if (cred->x509Credential.x509CACertFile) free(cred->x509Credential.x509CACertFile); + if (cred->x509Credential.x509CACrlFile) free(cred->x509Credential.x509CACrlFile); + if (cred->x509Credential.x509ClientCertFile) free(cred->x509Credential.x509ClientCertFile); + if (cred->x509Credential.x509ClientKeyFile) free(cred->x509Credential.x509ClientKeyFile); + free(cred); +} + rfbBool HandleVeNCryptAuth(rfbClient* client) { uint8_t major, minor, status; uint32_t authScheme; rfbBool anonTLS; -// gnutls_certificate_credentials_t x509_cred = NULL; + rfbCredential *cred = NULL; + rfbBool result = TRUE; if (!InitializeTLS()) return FALSE; @@ -499,7 +586,6 @@ HandleVeNCryptAuth(rfbClient* client) /* Get X509 Credentials if it's not anonymous */ if (!anonTLS) { - rfbCredential *cred; if (!client->GetCredential) { @@ -512,39 +598,18 @@ HandleVeNCryptAuth(rfbClient* client) rfbClientLog("Reading credential failed\n"); return FALSE; } - - /* TODO: don't just ignore this - x509_cred = CreateX509CertCredential(cred); - FreeX509Credential(cred); - if (!x509_cred) return FALSE; */ } /* Start up the TLS session */ - if (!InitializeTLSSession(client, anonTLS)) return FALSE; + if (!InitializeTLSSession(client, anonTLS, cred)) result = FALSE; - if (anonTLS) - { - if (!SetTLSAnonCredential(client)) return FALSE; - } - else - { -/* TODO: don't just ignore this - if ((ret = gnutls_credentials_set(client->tlsSession, GNUTLS_CRD_CERTIFICATE, x509_cred)) < 0) - { - rfbClientLog("Cannot set x509 credential: %s.\n", gnutls_strerror(ret)); - FreeTLS(client); */ - return FALSE; - // } - } - - if (!HandshakeTLS(client)) return FALSE; - - /* TODO: validate certificate */ + if (!HandshakeTLS(client)) result = FALSE; /* We are done here. The caller should continue with client->subAuthScheme * to do actual sub authentication. */ - return TRUE; + if (cred) FreeX509Credential(cred); + return result; } int -- cgit v1.2.1 From cb4e15c1aecbe283b87e4e97c1d485062ef7f571 Mon Sep 17 00:00:00 2001 From: simon Date: Fri, 21 Apr 2017 01:24:56 +0100 Subject: Added SASL authentication support Added SASL support to OpenSSL --- libvncclient/rfbproto.c | 26 ++ libvncclient/rfbsasl.c | 579 +++++++++++++++++++++++++++++++++++++++++++++ libvncclient/rfbsasl.h | 35 +++ libvncclient/sockets.c | 56 ++++- libvncclient/tls.h | 5 + libvncclient/tls_gnutls.c | 20 +- libvncclient/tls_none.c | 9 + libvncclient/tls_openssl.c | 18 ++ libvncclient/vncviewer.c | 13 + 9 files changed, 749 insertions(+), 12 deletions(-) create mode 100644 libvncclient/rfbsasl.c create mode 100644 libvncclient/rfbsasl.h (limited to 'libvncclient') diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index e099f1a..27589b8 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -66,6 +66,10 @@ #include #endif +#ifdef LIBVNCSERVER_HAVE_SASL +#include "rfbsasl.h" +#endif /* LIBVNCSERVER_HAVE_SASL */ + #include "minilzo.h" #include "tls.h" @@ -500,6 +504,9 @@ ReadSupportedSecurityType(rfbClient* client, uint32_t *result, rfbBool subAuth) #if defined(LIBVNCSERVER_HAVE_GNUTLS) || defined(LIBVNCSERVER_HAVE_LIBSSL) tAuth[loop]==rfbVeNCrypt || #endif +#ifdef LIBVNCSERVER_HAVE_SASL + tAuth[loop]==rfbSASL || +#endif /* LIBVNCSERVER_HAVE_SASL */ (tAuth[loop]==rfbARD && client->GetCredential) || (!subAuth && (tAuth[loop]==rfbTLS || (tAuth[loop]==rfbVeNCrypt && client->GetCredential)))) { @@ -1079,6 +1086,12 @@ InitialiseRFBConnection(rfbClient* client) if (!HandleVncAuth(client)) return FALSE; break; +#ifdef LIBVNCSERVER_HAVE_SASL + case rfbSASL: + if (!HandleSASLAuth(client)) return FALSE; + break; +#endif /* LIBVNCSERVER_HAVE_SASL */ + case rfbMSLogon: if (!HandleMSLogonAuth(client)) return FALSE; break; @@ -1117,6 +1130,12 @@ InitialiseRFBConnection(rfbClient* client) if (!HandleVncAuth(client)) return FALSE; break; +#ifdef LIBVNCSERVER_HAVE_SASL + case rfbSASL: + if (!HandleSASLAuth(client)) return FALSE; + break; +#endif /* LIBVNCSERVER_HAVE_SASL */ + default: rfbClientLog("Unknown sub authentication scheme from VNC server: %d\n", (int)subAuthScheme); @@ -1146,6 +1165,13 @@ InitialiseRFBConnection(rfbClient* client) if (!HandlePlainAuth(client)) return FALSE; break; +#ifdef LIBVNCSERVER_HAVE_SASL + case rfbVeNCryptX509SASL: + case rfbVeNCryptTLSSASL: + if (!HandleSASLAuth(client)) return FALSE; + break; +#endif /* LIBVNCSERVER_HAVE_SASL */ + default: rfbClientLog("Unknown sub authentication scheme from VNC server: %d\n", client->subAuthScheme); diff --git a/libvncclient/rfbsasl.c b/libvncclient/rfbsasl.c new file mode 100644 index 0000000..dc7d3bc --- /dev/null +++ b/libvncclient/rfbsasl.c @@ -0,0 +1,579 @@ +/* + * The software in this file is derived from the vncconnection.c source file + * from the GTK VNC Widget with modifications by S. Waterman + * for compatibility with libvncserver. The copyright and license + * statements below apply only to this source file and to no other parts of the + * libvncserver library. + * + * GTK VNC Widget + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2009-2010 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * rfbsasl.c - functions to deal with client side of the SASL protocol. + */ + +#ifdef __STRICT_ANSI__ +#define _BSD_SOURCE +#define _POSIX_SOURCE +#define _XOPEN_SOURCE 600 +#endif + +#include +#include + +#ifdef WIN32 +#undef SOCKET +#include +#define EWOULDBLOCK WSAEWOULDBLOCK +#define socklen_t int +#define close closesocket +#define read(sock,buf,len) recv(sock,buf,len,0) +#define write(sock,buf,len) send(sock,buf,len,0) +#ifdef LIBVNCSERVER_HAVE_WS2TCPIP_H +#undef socklen_t +#include +#endif /* LIBVNCSERVER_HAVE_WS2TCPIP_H */ +#else /* WIN32 */ +#include +#endif /* WIN32 */ + +#include "rfbsasl.h" + +#include "tls.h" + +#ifdef _MSC_VER +# define snprintf _snprintf /* MSVC went straight to the underscored syntax */ +#endif + +/* + * NB, keep in sync with similar method in qemud/remote.c + */ +static char *vnc_connection_addr_to_string(char *host, int port) +{ + char * buf = (char *)malloc(strlen(host) + 7); + sprintf(buf, "%s;%hu", host, port); + return buf; +} + +static int log_func(void *context, + int level, + const char *message) +{ + rfbClientLog("SASL: %s\n", message); + + return SASL_OK; +} + +static int user_callback_adapt(void *context, + int id, + const char **result, + unsigned *len) +{ + rfbClient* client = (rfbClient *)context; + + if (id != SASL_CB_AUTHNAME) { + rfbClientLog("Unrecognized SASL callback ID %d\n", id); + return SASL_FAIL; + } + + if (!client->GetUser) { + rfbClientLog("Client user callback not found\n"); + return SASL_FAIL; + } + + *result = client->GetUser(client); + + if (! *result) return SASL_FAIL; + /**len = strlen(*result);*/ + return SASL_OK; +} + +static int password_callback_adapt(sasl_conn_t *conn, + void * context, + int id, + sasl_secret_t **secret) +{ + rfbClient* client = (rfbClient *)context; + char * password; + + if (id != SASL_CB_PASS) { + rfbClientLog("Unrecognized SASL callback ID %d\n", id); + return SASL_FAIL; + } + + if (client->saslSecret) { /* If we've already got it just return it. */ + *secret = client->saslSecret; + return SASL_OK; + } + + if (!client->GetPassword) { + rfbClientLog("Client password callback not found\n"); + return SASL_FAIL; + } + + password = client->GetPassword(client); + + if (! password) return SASL_FAIL; + + sasl_secret_t *lsec = (sasl_secret_t *)malloc(sizeof(sasl_secret_t) + strlen(password)); + if (!lsec) { + rfbClientLog("Could not allocate sasl_secret_t\n"); + return SASL_FAIL; + } + + strcpy(lsec->data, password); + lsec->len = strlen(password); + client->saslSecret = lsec; + *secret = lsec; + + /* Clear client password */ + size_t i; + for (i = 0; i < lsec->len; i++) { + password[i] = '\0'; + } + free(password); + + return SASL_OK; +} + +#define SASL_MAX_MECHLIST_LEN 300 +#define SASL_MAX_DATA_LEN (1024 * 1024) + +/* Perform the SASL authentication process + */ +rfbBool +HandleSASLAuth(rfbClient *client) +{ + sasl_conn_t *saslconn = NULL; + sasl_security_properties_t secprops; + const char *clientout; + char *serverin = NULL; + unsigned int clientoutlen, serverinlen; + int err, complete = 0; + char *localAddr = NULL, *remoteAddr = NULL; + const void *val; + sasl_ssf_t ssf; + sasl_callback_t saslcb[] = { + {SASL_CB_LOG, (void *)log_func, NULL}, + {SASL_CB_AUTHNAME, client->GetUser ? (void *)user_callback_adapt : NULL, client}, + {SASL_CB_PASS, client->GetPassword ? (void *)password_callback_adapt : NULL, client}, + { .id = 0 }, + }; + sasl_interact_t *interact = NULL; + uint32_t mechlistlen; + char *mechlist; + char *wantmech; + const char *mechname; + rfbBool ret; + + client->saslconn = NULL; + + /* Sets up the SASL library as a whole */ + err = sasl_client_init(NULL); + rfbClientLog("Client initialize SASL authentication %d\n", err); + if (err != SASL_OK) { + rfbClientLog("failed to initialize SASL library: %d (%s)\n", + err, sasl_errstring(err, NULL, NULL)); + goto error; + } + + /* Get local address in form IPADDR:PORT */ + struct sockaddr_storage localAddress; + socklen_t addressLength = sizeof(localAddress); + char buf[INET6_ADDRSTRLEN]; + int port; + + if (getsockname(client->sock, (struct sockaddr*)&localAddress, &addressLength)) { + rfbClientLog("failed to get local address\n"); + goto error; + } + + if (localAddress.ss_family == AF_INET) { + struct sockaddr_in *sa_in = (struct sockaddr_in*)&localAddress; + inet_ntop(AF_INET, &(sa_in->sin_addr), buf, INET_ADDRSTRLEN); + port = ntohs(sa_in->sin_port); + localAddr = vnc_connection_addr_to_string(buf, port); + } else if (localAddress.ss_family == AF_INET6) { + struct sockaddr_in6 *sa_in = (struct sockaddr_in6*)&localAddress; + inet_ntop(AF_INET6, &(sa_in->sin6_addr), buf, INET6_ADDRSTRLEN); + port = ntohs(sa_in->sin6_port); + localAddr = vnc_connection_addr_to_string(buf, port); + } else { + rfbClientLog("failed to get local address\n"); + goto error; + } + + /* Get remote address in form IPADDR:PORT */ + remoteAddr = vnc_connection_addr_to_string(client->serverHost, client->serverPort); + + rfbClientLog("Client SASL new host:'%s' local:'%s' remote:'%s'\n", client->serverHost, localAddr, remoteAddr); + + /* Setup a handle for being a client */ + err = sasl_client_new("vnc", + client->serverHost, + localAddr, + remoteAddr, + saslcb, + SASL_SUCCESS_DATA, + &saslconn); + free(localAddr); + free(remoteAddr); + + if (err != SASL_OK) { + rfbClientLog("Failed to create SASL client context: %d (%s)\n", + err, sasl_errstring(err, NULL, NULL)); + goto error; + } + + /* Initialize some connection props we care about */ + if (client->tlsSession) { + if (!(ssf = (sasl_ssf_t)GetTLSCipherBits(client))) { + rfbClientLog("%s", "invalid cipher size for TLS session\n"); + goto error; + } + + rfbClientLog("Setting external SSF %d\n", ssf); + err = sasl_setprop(saslconn, SASL_SSF_EXTERNAL, &ssf); + if (err != SASL_OK) { + rfbClientLog("cannot set external SSF %d (%s)\n", + err, sasl_errstring(err, NULL, NULL)); + goto error; + } + } + + memset (&secprops, 0, sizeof secprops); + /* If we've got TLS, we don't care about SSF */ + secprops.min_ssf = client->tlsSession ? 0 : 56; /* Equiv to DES supported by all Kerberos */ + secprops.max_ssf = client->tlsSession ? 0 : 100000; /* Very strong ! AES == 256 */ + secprops.maxbufsize = 100000; + /* If we're not TLS, then forbid any anonymous or trivially crackable auth */ + secprops.security_flags = client->tlsSession ? 0 : + SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT; + + err = sasl_setprop(saslconn, SASL_SEC_PROPS, &secprops); + if (err != SASL_OK) { + rfbClientLog("cannot set security props %d (%s)\n", + err, sasl_errstring(err, NULL, NULL)); + goto error; + } + + /* Get the supported mechanisms from the server */ + if (!ReadFromRFBServer(client, (char *)&mechlistlen, 4)) { + rfbClientLog("failed to read mechlistlen\n"); + goto error; + } + mechlistlen = rfbClientSwap32IfLE(mechlistlen); + rfbClientLog("mechlistlen is %d\n", mechlistlen); + if (mechlistlen > SASL_MAX_MECHLIST_LEN) { + rfbClientLog("mechlistlen %d too long\n", mechlistlen); + goto error; + } + + mechlist = malloc(mechlistlen+1); + if (!ReadFromRFBServer(client, mechlist, mechlistlen)) { + free(mechlist); + goto error; + } + mechlist[mechlistlen] = '\0'; + + /* Allow the client to influence the mechanism selected. */ + if (client->GetSASLMechanism) { + wantmech = client->GetSASLMechanism(client, mechlist); + + if (wantmech && *wantmech != 0) { + if (strstr(mechlist, wantmech) == NULL) { + rfbClientLog("Client requested SASL mechanism %s not supported by server\n", + wantmech); + free(mechlist); + free(wantmech); + goto error; + } else { + free(mechlist); + mechlist = wantmech; + } + } + } + + rfbClientLog("Client start negotiation mechlist '%s'\n", mechlist); + + restart: + /* Start the auth negotiation on the client end first */ + err = sasl_client_start(saslconn, + mechlist, + &interact, + &clientout, + &clientoutlen, + &mechname); + if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) { + rfbClientLog("Failed to start SASL negotiation: %d (%s)\n", + err, sasl_errdetail(saslconn)); + free(mechlist); + mechlist = NULL; + goto error; + } + + /* Need to gather some credentials from the client */ + if (err == SASL_INTERACT) { + rfbClientLog("User interaction required but not currently supported\n"); + goto error; + } + + rfbClientLog("Server start negotiation with mech %s. Data %d bytes %p '%s'\n", + mechname, clientoutlen, clientout, clientout); + + if (clientoutlen > SASL_MAX_DATA_LEN) { + rfbClientLog("SASL negotiation data too long: %d bytes\n", + clientoutlen); + goto error; + } + + /* Send back the chosen mechname */ + uint32_t mechnamelen = rfbClientSwap32IfLE(strlen(mechname)); + if (!WriteToRFBServer(client, (char *)&mechnamelen, 4)) goto error; + if (!WriteToRFBServer(client, (char *)mechname, strlen(mechname))) goto error; + + /* NB, distinction of NULL vs "" is *critical* in SASL */ + if (clientout) { + uint32_t colsw = rfbClientSwap32IfLE(clientoutlen + 1); + if (!WriteToRFBServer(client, (char *)&colsw, 4)) goto error; + if (!WriteToRFBServer(client, (char *)clientout, clientoutlen + 1)) goto error; + } else { + uint32_t temp = 0; + if (!WriteToRFBServer(client, (char *)&temp, 4)) goto error; + } + + rfbClientLog("%s", "Getting sever start negotiation reply\n"); + /* Read the 'START' message reply from server */ + if (!ReadFromRFBServer(client, (char *)&serverinlen, 4)) goto error; + serverinlen = rfbClientSwap32IfLE(serverinlen); + + if (serverinlen > SASL_MAX_DATA_LEN) { + rfbClientLog("SASL negotiation data too long: %d bytes\n", + serverinlen); + goto error; + } + + /* NB, distinction of NULL vs "" is *critical* in SASL */ + if (serverinlen) { + serverin = malloc(serverinlen); + if (!ReadFromRFBServer(client, serverin, serverinlen)) goto error; + serverin[serverinlen-1] = '\0'; + serverinlen--; + } else { + serverin = NULL; + } + if (!ReadFromRFBServer(client, (char *)&complete, 1)) goto error; + + rfbClientLog("Client start result complete: %d. Data %d bytes %p '%s'\n", + complete, serverinlen, serverin, serverin); + + /* Loop-the-loop... + * Even if the server has completed, the client must *always* do at least one step + * in this loop to verify the server isn't lying about something. Mutual auth */ + for (;;) { + restep: + err = sasl_client_step(saslconn, + serverin, + serverinlen, + &interact, + &clientout, + &clientoutlen); + if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) { + rfbClientLog("Failed SASL step: %d (%s)\n", + err, sasl_errdetail(saslconn)); + goto error; + } + + /* Need to gather some credentials from the client */ + if (err == SASL_INTERACT) { + rfbClientLog("User interaction required but not currently supported\n"); + goto error; + } + + if (serverin) { + free(serverin); + serverin = NULL; + } + + rfbClientLog("Client step result %d. Data %d bytes %p '%s'\n", err, clientoutlen, clientout, clientout); + + /* Previous server call showed completion & we're now locally complete too */ + if (complete && err == SASL_OK) + break; + + /* Not done, prepare to talk with the server for another iteration */ + + /* NB, distinction of NULL vs "" is *critical* in SASL */ + if (clientout) { + uint32_t colsw = rfbClientSwap32IfLE(clientoutlen + 1); + if (!WriteToRFBServer(client, (char *)&colsw, 4)) goto error; + if (!WriteToRFBServer(client, (char *)clientout, clientoutlen + 1)) goto error; + } else { + uint32_t temp = 0; + if (!WriteToRFBServer(client, (char *)&temp, 4)) goto error; + } + + rfbClientLog("Server step with %d bytes %p\n", clientoutlen, clientout); + + if (!ReadFromRFBServer(client, (char *)&serverinlen, 4)) goto error; + serverinlen = rfbClientSwap32IfLE(serverinlen); + + if (serverinlen > SASL_MAX_DATA_LEN) { + rfbClientLog("SASL negotiation data too long: %d bytes\n", + serverinlen); + goto error; + } + + /* NB, distinction of NULL vs "" is *critical* in SASL */ + if (serverinlen) { + serverin = malloc(serverinlen); + if (!ReadFromRFBServer(client, serverin, serverinlen)) goto error; + serverin[serverinlen-1] = '\0'; + serverinlen--; + } else { + serverin = NULL; + } + if (!ReadFromRFBServer(client, (char *)&complete, 1)) goto error; + + rfbClientLog("Client step result complete: %d. Data %d bytes %p '%s'\n", + complete, serverinlen, serverin, serverin); + + /* This server call shows complete, and earlier client step was OK */ + if (complete && err == SASL_OK) { + free(serverin); + serverin = NULL; + break; + } + } + + /* Check for suitable SSF if non-TLS */ + if (!client->tlsSession) { + err = sasl_getprop(saslconn, SASL_SSF, &val); + if (err != SASL_OK) { + rfbClientLog("cannot query SASL ssf on connection %d (%s)\n", + err, sasl_errstring(err, NULL, NULL)); + goto error; + } + ssf = *(const int *)val; + rfbClientLog("SASL SSF value %d\n", ssf); + if (ssf < 56) { /* 56 == DES level, good for Kerberos */ + rfbClientLog("negotiation SSF %d was not strong enough\n", ssf); + goto error; + } + } + + rfbClientLog("%s", "SASL authentication complete\n"); + + uint32_t result; + if (!ReadFromRFBServer(client, (char *)&result, 4)) { + rfbClientLog("Failed to read authentication result\n"); + goto error; + } + result = rfbClientSwap32IfLE(result); + + if (result != 0) { + rfbClientLog("Authentication failure\n"); + goto error; + } + rfbClientLog("Authentication successful - switching to SSF\n"); + + /* This must come *after* check-auth-result, because the former + * is defined to be sent unencrypted, and setting saslconn turns + * on the SSF layer encryption processing */ + client->saslconn = saslconn; + + /* Clear SASL secret from memory if set - it'll be free'd on dispose */ + if (client->saslSecret) { + size_t i; + for (i = 0; i < client->saslSecret->len; i++) + client->saslSecret->data[i] = '\0'; + client->saslSecret->len = 0; + } + + return TRUE; + + error: + if (client->saslSecret) { + size_t i; + for (i = 0; i < client->saslSecret->len; i++) + client->saslSecret->data[i] = '\0'; + client->saslSecret->len = 0; + } + + if (saslconn) + sasl_dispose(&saslconn); + return FALSE; +} + +int +ReadFromSASL(rfbClient* client, char *out, unsigned int n) +{ + size_t want; + + if (client->saslDecoded == NULL) { + char *encoded; + int encodedLen; + int err, ret; + + encodedLen = 8192; + encoded = (char *)malloc(encodedLen); + + ret = read(client->sock, encoded, encodedLen); + if (ret < 0) { + free(encoded); + return ret; + } + if (ret == 0) { + free(encoded); + errno = EIO; + return -EIO; + } + + err = sasl_decode(client->saslconn, encoded, ret, + &client->saslDecoded, &client->saslDecodedLength); + free(encoded); + if (err != SASL_OK) { + rfbClientLog("Failed to decode SASL data %s\n", + sasl_errstring(err, NULL, NULL)); + return -EINVAL; + } + client->saslDecodedOffset = 0; + } + + want = client->saslDecodedLength - client->saslDecodedOffset; + if (want > n) + want = n; + + memcpy(out, + client->saslDecoded + client->saslDecodedOffset, + want); + client->saslDecodedOffset += want; + if (client->saslDecodedOffset == client->saslDecodedLength) { + client->saslDecodedLength = client->saslDecodedOffset = 0; + client->saslDecoded = NULL; + } + + if (!want) { + errno = EAGAIN; + return -EAGAIN; + } + + return want; +} diff --git a/libvncclient/rfbsasl.h b/libvncclient/rfbsasl.h new file mode 100644 index 0000000..8231096 --- /dev/null +++ b/libvncclient/rfbsasl.h @@ -0,0 +1,35 @@ +#ifndef RFBSASL_H +#define RFBSASL_H + +/* + * Copyright (C) 2017 S. Waterman. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include + +/* + * Perform the SASL authentication process + */ +rfbBool HandleSASLAuth(rfbClient *client); + +/* + * Read from SASL when the SASL SSF is in use. + */ +int ReadFromSASL(rfbClient* client, char *out, unsigned int n); + +#endif /* RFBSASL_H */ diff --git a/libvncclient/sockets.c b/libvncclient/sockets.c index 1019580..8ed51a5 100644 --- a/libvncclient/sockets.c +++ b/libvncclient/sockets.c @@ -59,6 +59,10 @@ #endif #include "tls.h" +#ifdef LIBVNCSERVER_HAVE_SASL +#include "rfbsasl.h" +#endif /* LIBVNCSERVER_HAVE_SASL */ + #ifdef _MSC_VER # define snprintf _snprintf #endif @@ -154,16 +158,24 @@ ReadFromRFBServer(rfbClient* client, char *out, unsigned int n) while (client->buffered < n) { int i; - if (client->tlsSession) { + if (client->tlsSession) i = ReadFromTLS(client, client->buf + client->buffered, RFB_BUF_SIZE - client->buffered); - } else { + else +#ifdef LIBVNCSERVER_HAVE_SASL + if (client->saslconn) + i = ReadFromSASL(client, client->buf + client->buffered, RFB_BUF_SIZE - client->buffered); + else { +#endif /* LIBVNCSERVER_HAVE_SASL */ i = read(client->sock, client->buf + client->buffered, RFB_BUF_SIZE - client->buffered); +#ifdef WIN32 + if (i < 0) errno=WSAGetLastError(); +#endif +#ifdef LIBVNCSERVER_HAVE_SASL } +#endif + if (i <= 0) { if (i < 0) { -#ifdef WIN32 - errno=WSAGetLastError(); -#endif if (errno == EWOULDBLOCK || errno == EAGAIN) { /* TODO: ProcessXtEvents(); @@ -192,11 +204,15 @@ ReadFromRFBServer(rfbClient* client, char *out, unsigned int n) while (n > 0) { int i; - if (client->tlsSession) { + if (client->tlsSession) i = ReadFromTLS(client, out, n); - } else { + else +#ifdef LIBVNCSERVER_HAVE_SASL + if (client->saslconn) + i = ReadFromSASL(client, out, n); + else +#endif i = read(client->sock, out, n); - } if (i <= 0) { if (i < 0) { @@ -248,6 +264,12 @@ WriteToRFBServer(rfbClient* client, char *buf, int n) fd_set fds; int i = 0; int j; + const char *obuf = buf; +#ifdef LIBVNCSERVER_HAVE_SASL + const char *output; + unsigned int outputlen; + int err; +#endif /* LIBVNCSERVER_HAVE_SASL */ if (client->serverPort==-1) return TRUE; /* vncrec playing */ @@ -259,9 +281,23 @@ WriteToRFBServer(rfbClient* client, char *buf, int n) return TRUE; } +#ifdef LIBVNCSERVER_HAVE_SASL + if (client->saslconn) { + err = sasl_encode(client->saslconn, + buf, n, + &output, &outputlen); + if (err != SASL_OK) { + rfbClientLog("Failed to encode SASL data %s", + sasl_errstring(err, NULL, NULL)); + return FALSE; + } + obuf = output; + n = outputlen; + } +#endif /* LIBVNCSERVER_HAVE_SASL */ while (i < n) { - j = write(client->sock, buf + i, (n - i)); + j = write(client->sock, obuf + i, (n - i)); if (j <= 0) { if (j < 0) { #ifdef WIN32 @@ -294,8 +330,6 @@ WriteToRFBServer(rfbClient* client, char *buf, int n) return TRUE; } - - static int initSockets() { #ifdef WIN32 WSADATA trash; diff --git a/libvncclient/tls.h b/libvncclient/tls.h index 48d159b..a5a2ac6 100644 --- a/libvncclient/tls.h +++ b/libvncclient/tls.h @@ -48,4 +48,9 @@ int WriteToTLS(rfbClient* client, char *buf, unsigned int n); /* Free TLS resources */ void FreeTLS(rfbClient* client); +#ifdef LIBVNCSERVER_HAVE_SASL +/* Get the number of bits in the current cipher */ +int GetTLSCipherBits(rfbClient* client); +#endif /* LIBVNCSERVER_HAVE_SASL */ + #endif /* TLS_H */ diff --git a/libvncclient/tls_gnutls.c b/libvncclient/tls_gnutls.c index b9ffe89..75bde35 100644 --- a/libvncclient/tls_gnutls.c +++ b/libvncclient/tls_gnutls.c @@ -170,7 +170,7 @@ InitializeTLSSession(rfbClient* client, rfbBool anonTLS) static rfbBool SetTLSAnonCredential(rfbClient* client) { - gnutls_anon_client_credentials anonCred; + gnutls_anon_client_credentials_t anonCred; int ret; if ((ret = gnutls_anon_allocate_client_credentials(&anonCred)) < 0 || @@ -252,6 +252,10 @@ ReadVeNCryptSecurityType(rfbClient* client, uint32_t *result) if (t==rfbVeNCryptTLSNone || t==rfbVeNCryptTLSVNC || t==rfbVeNCryptTLSPlain || +#ifdef LIBVNCSERVER_HAVE_SASL + t==rfbVeNCryptTLSSASL || + t==rfbVeNCryptX509SASL || +#endif /*LIBVNCSERVER_HAVE_SASL */ t==rfbVeNCryptX509None || t==rfbVeNCryptX509VNC || t==rfbVeNCryptX509Plain) @@ -411,6 +415,9 @@ HandleVeNCryptAuth(rfbClient* client) case rfbVeNCryptTLSNone: case rfbVeNCryptTLSVNC: case rfbVeNCryptTLSPlain: +#ifdef LIBVNCSERVER_HAVE_SASL + case rfbVeNCryptTLSSASL: +#endif /* LIBVNCSERVER_HAVE_SASL */ anonTLS = TRUE; break; default: @@ -535,3 +542,14 @@ void FreeTLS(rfbClient* client) client->tlsSession = NULL; } } + +#ifdef LIBVNCSERVER_HAVE_SASL +int +GetTLSCipherBits(rfbClient* client) +{ + gnutls_cipher_algorithm_t cipher = gnutls_cipher_get((gnutls_session_t)client->tlsSession); + + return gnutls_cipher_get_key_size(cipher) * 8; +} +#endif /* LIBVNCSERVER_HAVE_SASL */ + diff --git a/libvncclient/tls_none.c b/libvncclient/tls_none.c index 91a9f93..4dfcb27 100644 --- a/libvncclient/tls_none.c +++ b/libvncclient/tls_none.c @@ -56,3 +56,12 @@ void FreeTLS(rfbClient* client) } +#ifdef LIBVNCSERVER_HAVE_SASL +int +GetTLSCipherBits(rfbClient* client) +{ + rfbClientLog("TLS is not supported.\n"); + return 0; +} +#endif /* LIBVNCSERVER_HAVE_SASL */ + diff --git a/libvncclient/tls_openssl.c b/libvncclient/tls_openssl.c index 1b6c986..4f758d0 100644 --- a/libvncclient/tls_openssl.c +++ b/libvncclient/tls_openssl.c @@ -397,6 +397,10 @@ ReadVeNCryptSecurityType(rfbClient* client, uint32_t *result) if (t==rfbVeNCryptTLSNone || t==rfbVeNCryptTLSVNC || t==rfbVeNCryptTLSPlain || +#ifdef LIBVNCSERVER_HAVE_SASL + t==rfbVeNCryptTLSSASL || + t==rfbVeNCryptX509SASL || +#endif /*LIBVNCSERVER_HAVE_SASL */ t==rfbVeNCryptX509None || t==rfbVeNCryptX509VNC || t==rfbVeNCryptX509Plain) @@ -489,6 +493,9 @@ HandleVeNCryptAuth(rfbClient* client) case rfbVeNCryptTLSNone: case rfbVeNCryptTLSVNC: case rfbVeNCryptTLSPlain: +#ifdef LIBVNCSERVER_HAVE_SASL + case rfbVeNCryptTLSSASL: +#endif /* LIBVNCSERVER_HAVE_SASL */ anonTLS = TRUE; break; default: @@ -614,3 +621,14 @@ void FreeTLS(rfbClient* client) SSL_free(client->tlsSession); } +#ifdef LIBVNCSERVER_HAVE_SASL +int GetTLSCipherBits(rfbClient* client) +{ + SSL *ssl = (SSL *)(client->tlsSession); + + const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl); + + return SSL_CIPHER_get_bits(cipher, NULL); +} +#endif /* LIBVNCSERVER_HAVE_SASL */ + diff --git a/libvncclient/vncviewer.c b/libvncclient/vncviewer.c index 780a1cb..407b4a5 100644 --- a/libvncclient/vncviewer.c +++ b/libvncclient/vncviewer.c @@ -350,6 +350,13 @@ rfbClient* rfbGetClient(int bitsPerSample,int samplesPerPixel, client->listen6Sock = -1; client->listen6Address = NULL; client->clientAuthSchemes = NULL; + +#ifdef LIBVNCSERVER_HAVE_SASL + client->GetSASLMechanism = NULL; + client->GetUser = NULL; + client->saslSecret = NULL; +#endif /* LIBVNCSERVER_HAVE_SASL */ + return client; } @@ -534,5 +541,11 @@ void rfbClientCleanup(rfbClient* client) { free(client->destHost); if (client->clientAuthSchemes) free(client->clientAuthSchemes); + +#ifdef LIBVNCSERVER_HAVE_SASL + if (client->saslSecret) + free(client->saslSecret); +#endif /* LIBVNCSERVER_HAVE_SASL */ + free(client); } -- cgit v1.2.1 From df11e806a31ec0fe0f3f58917ed1e98769adb910 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sat, 2 Sep 2017 17:20:58 +0200 Subject: Move HAVE_SASL #ifdefs into header file to have less LOC --- libvncclient/rfbproto.c | 3 --- libvncclient/rfbsasl.h | 4 ++++ libvncclient/sockets.c | 3 --- 3 files changed, 4 insertions(+), 6 deletions(-) (limited to 'libvncclient') diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index 27589b8..572af9d 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -66,10 +66,7 @@ #include #endif -#ifdef LIBVNCSERVER_HAVE_SASL #include "rfbsasl.h" -#endif /* LIBVNCSERVER_HAVE_SASL */ - #include "minilzo.h" #include "tls.h" diff --git a/libvncclient/rfbsasl.h b/libvncclient/rfbsasl.h index 8231096..2936364 100644 --- a/libvncclient/rfbsasl.h +++ b/libvncclient/rfbsasl.h @@ -20,6 +20,8 @@ * USA. */ +#ifdef LIBVNCSERVER_HAVE_SASL + #include /* @@ -32,4 +34,6 @@ rfbBool HandleSASLAuth(rfbClient *client); */ int ReadFromSASL(rfbClient* client, char *out, unsigned int n); +#endif /* LIBVNCSERVER_HAVE_SASL */ + #endif /* RFBSASL_H */ diff --git a/libvncclient/sockets.c b/libvncclient/sockets.c index 8ed51a5..2d505c7 100644 --- a/libvncclient/sockets.c +++ b/libvncclient/sockets.c @@ -58,10 +58,7 @@ #include #endif #include "tls.h" - -#ifdef LIBVNCSERVER_HAVE_SASL #include "rfbsasl.h" -#endif /* LIBVNCSERVER_HAVE_SASL */ #ifdef _MSC_VER # define snprintf _snprintf -- cgit v1.2.1 From 6814e946e0afed20a6ef0f45a9bcbfeda2e77706 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sat, 2 Sep 2017 18:05:46 +0200 Subject: libvncclient: rename rfbsasl.[c|h] to sasl.[c|h] to be in line with naming of other files --- libvncclient/rfbproto.c | 2 +- libvncclient/rfbsasl.c | 579 ------------------------------------------------ libvncclient/rfbsasl.h | 39 ---- libvncclient/sasl.c | 579 ++++++++++++++++++++++++++++++++++++++++++++++++ libvncclient/sasl.h | 39 ++++ libvncclient/sockets.c | 2 +- 6 files changed, 620 insertions(+), 620 deletions(-) delete mode 100644 libvncclient/rfbsasl.c delete mode 100644 libvncclient/rfbsasl.h create mode 100644 libvncclient/sasl.c create mode 100644 libvncclient/sasl.h (limited to 'libvncclient') diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index 572af9d..df8b6d0 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -66,7 +66,7 @@ #include #endif -#include "rfbsasl.h" +#include "sasl.h" #include "minilzo.h" #include "tls.h" diff --git a/libvncclient/rfbsasl.c b/libvncclient/rfbsasl.c deleted file mode 100644 index dc7d3bc..0000000 --- a/libvncclient/rfbsasl.c +++ /dev/null @@ -1,579 +0,0 @@ -/* - * The software in this file is derived from the vncconnection.c source file - * from the GTK VNC Widget with modifications by S. Waterman - * for compatibility with libvncserver. The copyright and license - * statements below apply only to this source file and to no other parts of the - * libvncserver library. - * - * GTK VNC Widget - * - * Copyright (C) 2006 Anthony Liguori - * Copyright (C) 2009-2010 Daniel P. Berrange - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.0 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* - * rfbsasl.c - functions to deal with client side of the SASL protocol. - */ - -#ifdef __STRICT_ANSI__ -#define _BSD_SOURCE -#define _POSIX_SOURCE -#define _XOPEN_SOURCE 600 -#endif - -#include -#include - -#ifdef WIN32 -#undef SOCKET -#include -#define EWOULDBLOCK WSAEWOULDBLOCK -#define socklen_t int -#define close closesocket -#define read(sock,buf,len) recv(sock,buf,len,0) -#define write(sock,buf,len) send(sock,buf,len,0) -#ifdef LIBVNCSERVER_HAVE_WS2TCPIP_H -#undef socklen_t -#include -#endif /* LIBVNCSERVER_HAVE_WS2TCPIP_H */ -#else /* WIN32 */ -#include -#endif /* WIN32 */ - -#include "rfbsasl.h" - -#include "tls.h" - -#ifdef _MSC_VER -# define snprintf _snprintf /* MSVC went straight to the underscored syntax */ -#endif - -/* - * NB, keep in sync with similar method in qemud/remote.c - */ -static char *vnc_connection_addr_to_string(char *host, int port) -{ - char * buf = (char *)malloc(strlen(host) + 7); - sprintf(buf, "%s;%hu", host, port); - return buf; -} - -static int log_func(void *context, - int level, - const char *message) -{ - rfbClientLog("SASL: %s\n", message); - - return SASL_OK; -} - -static int user_callback_adapt(void *context, - int id, - const char **result, - unsigned *len) -{ - rfbClient* client = (rfbClient *)context; - - if (id != SASL_CB_AUTHNAME) { - rfbClientLog("Unrecognized SASL callback ID %d\n", id); - return SASL_FAIL; - } - - if (!client->GetUser) { - rfbClientLog("Client user callback not found\n"); - return SASL_FAIL; - } - - *result = client->GetUser(client); - - if (! *result) return SASL_FAIL; - /**len = strlen(*result);*/ - return SASL_OK; -} - -static int password_callback_adapt(sasl_conn_t *conn, - void * context, - int id, - sasl_secret_t **secret) -{ - rfbClient* client = (rfbClient *)context; - char * password; - - if (id != SASL_CB_PASS) { - rfbClientLog("Unrecognized SASL callback ID %d\n", id); - return SASL_FAIL; - } - - if (client->saslSecret) { /* If we've already got it just return it. */ - *secret = client->saslSecret; - return SASL_OK; - } - - if (!client->GetPassword) { - rfbClientLog("Client password callback not found\n"); - return SASL_FAIL; - } - - password = client->GetPassword(client); - - if (! password) return SASL_FAIL; - - sasl_secret_t *lsec = (sasl_secret_t *)malloc(sizeof(sasl_secret_t) + strlen(password)); - if (!lsec) { - rfbClientLog("Could not allocate sasl_secret_t\n"); - return SASL_FAIL; - } - - strcpy(lsec->data, password); - lsec->len = strlen(password); - client->saslSecret = lsec; - *secret = lsec; - - /* Clear client password */ - size_t i; - for (i = 0; i < lsec->len; i++) { - password[i] = '\0'; - } - free(password); - - return SASL_OK; -} - -#define SASL_MAX_MECHLIST_LEN 300 -#define SASL_MAX_DATA_LEN (1024 * 1024) - -/* Perform the SASL authentication process - */ -rfbBool -HandleSASLAuth(rfbClient *client) -{ - sasl_conn_t *saslconn = NULL; - sasl_security_properties_t secprops; - const char *clientout; - char *serverin = NULL; - unsigned int clientoutlen, serverinlen; - int err, complete = 0; - char *localAddr = NULL, *remoteAddr = NULL; - const void *val; - sasl_ssf_t ssf; - sasl_callback_t saslcb[] = { - {SASL_CB_LOG, (void *)log_func, NULL}, - {SASL_CB_AUTHNAME, client->GetUser ? (void *)user_callback_adapt : NULL, client}, - {SASL_CB_PASS, client->GetPassword ? (void *)password_callback_adapt : NULL, client}, - { .id = 0 }, - }; - sasl_interact_t *interact = NULL; - uint32_t mechlistlen; - char *mechlist; - char *wantmech; - const char *mechname; - rfbBool ret; - - client->saslconn = NULL; - - /* Sets up the SASL library as a whole */ - err = sasl_client_init(NULL); - rfbClientLog("Client initialize SASL authentication %d\n", err); - if (err != SASL_OK) { - rfbClientLog("failed to initialize SASL library: %d (%s)\n", - err, sasl_errstring(err, NULL, NULL)); - goto error; - } - - /* Get local address in form IPADDR:PORT */ - struct sockaddr_storage localAddress; - socklen_t addressLength = sizeof(localAddress); - char buf[INET6_ADDRSTRLEN]; - int port; - - if (getsockname(client->sock, (struct sockaddr*)&localAddress, &addressLength)) { - rfbClientLog("failed to get local address\n"); - goto error; - } - - if (localAddress.ss_family == AF_INET) { - struct sockaddr_in *sa_in = (struct sockaddr_in*)&localAddress; - inet_ntop(AF_INET, &(sa_in->sin_addr), buf, INET_ADDRSTRLEN); - port = ntohs(sa_in->sin_port); - localAddr = vnc_connection_addr_to_string(buf, port); - } else if (localAddress.ss_family == AF_INET6) { - struct sockaddr_in6 *sa_in = (struct sockaddr_in6*)&localAddress; - inet_ntop(AF_INET6, &(sa_in->sin6_addr), buf, INET6_ADDRSTRLEN); - port = ntohs(sa_in->sin6_port); - localAddr = vnc_connection_addr_to_string(buf, port); - } else { - rfbClientLog("failed to get local address\n"); - goto error; - } - - /* Get remote address in form IPADDR:PORT */ - remoteAddr = vnc_connection_addr_to_string(client->serverHost, client->serverPort); - - rfbClientLog("Client SASL new host:'%s' local:'%s' remote:'%s'\n", client->serverHost, localAddr, remoteAddr); - - /* Setup a handle for being a client */ - err = sasl_client_new("vnc", - client->serverHost, - localAddr, - remoteAddr, - saslcb, - SASL_SUCCESS_DATA, - &saslconn); - free(localAddr); - free(remoteAddr); - - if (err != SASL_OK) { - rfbClientLog("Failed to create SASL client context: %d (%s)\n", - err, sasl_errstring(err, NULL, NULL)); - goto error; - } - - /* Initialize some connection props we care about */ - if (client->tlsSession) { - if (!(ssf = (sasl_ssf_t)GetTLSCipherBits(client))) { - rfbClientLog("%s", "invalid cipher size for TLS session\n"); - goto error; - } - - rfbClientLog("Setting external SSF %d\n", ssf); - err = sasl_setprop(saslconn, SASL_SSF_EXTERNAL, &ssf); - if (err != SASL_OK) { - rfbClientLog("cannot set external SSF %d (%s)\n", - err, sasl_errstring(err, NULL, NULL)); - goto error; - } - } - - memset (&secprops, 0, sizeof secprops); - /* If we've got TLS, we don't care about SSF */ - secprops.min_ssf = client->tlsSession ? 0 : 56; /* Equiv to DES supported by all Kerberos */ - secprops.max_ssf = client->tlsSession ? 0 : 100000; /* Very strong ! AES == 256 */ - secprops.maxbufsize = 100000; - /* If we're not TLS, then forbid any anonymous or trivially crackable auth */ - secprops.security_flags = client->tlsSession ? 0 : - SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT; - - err = sasl_setprop(saslconn, SASL_SEC_PROPS, &secprops); - if (err != SASL_OK) { - rfbClientLog("cannot set security props %d (%s)\n", - err, sasl_errstring(err, NULL, NULL)); - goto error; - } - - /* Get the supported mechanisms from the server */ - if (!ReadFromRFBServer(client, (char *)&mechlistlen, 4)) { - rfbClientLog("failed to read mechlistlen\n"); - goto error; - } - mechlistlen = rfbClientSwap32IfLE(mechlistlen); - rfbClientLog("mechlistlen is %d\n", mechlistlen); - if (mechlistlen > SASL_MAX_MECHLIST_LEN) { - rfbClientLog("mechlistlen %d too long\n", mechlistlen); - goto error; - } - - mechlist = malloc(mechlistlen+1); - if (!ReadFromRFBServer(client, mechlist, mechlistlen)) { - free(mechlist); - goto error; - } - mechlist[mechlistlen] = '\0'; - - /* Allow the client to influence the mechanism selected. */ - if (client->GetSASLMechanism) { - wantmech = client->GetSASLMechanism(client, mechlist); - - if (wantmech && *wantmech != 0) { - if (strstr(mechlist, wantmech) == NULL) { - rfbClientLog("Client requested SASL mechanism %s not supported by server\n", - wantmech); - free(mechlist); - free(wantmech); - goto error; - } else { - free(mechlist); - mechlist = wantmech; - } - } - } - - rfbClientLog("Client start negotiation mechlist '%s'\n", mechlist); - - restart: - /* Start the auth negotiation on the client end first */ - err = sasl_client_start(saslconn, - mechlist, - &interact, - &clientout, - &clientoutlen, - &mechname); - if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) { - rfbClientLog("Failed to start SASL negotiation: %d (%s)\n", - err, sasl_errdetail(saslconn)); - free(mechlist); - mechlist = NULL; - goto error; - } - - /* Need to gather some credentials from the client */ - if (err == SASL_INTERACT) { - rfbClientLog("User interaction required but not currently supported\n"); - goto error; - } - - rfbClientLog("Server start negotiation with mech %s. Data %d bytes %p '%s'\n", - mechname, clientoutlen, clientout, clientout); - - if (clientoutlen > SASL_MAX_DATA_LEN) { - rfbClientLog("SASL negotiation data too long: %d bytes\n", - clientoutlen); - goto error; - } - - /* Send back the chosen mechname */ - uint32_t mechnamelen = rfbClientSwap32IfLE(strlen(mechname)); - if (!WriteToRFBServer(client, (char *)&mechnamelen, 4)) goto error; - if (!WriteToRFBServer(client, (char *)mechname, strlen(mechname))) goto error; - - /* NB, distinction of NULL vs "" is *critical* in SASL */ - if (clientout) { - uint32_t colsw = rfbClientSwap32IfLE(clientoutlen + 1); - if (!WriteToRFBServer(client, (char *)&colsw, 4)) goto error; - if (!WriteToRFBServer(client, (char *)clientout, clientoutlen + 1)) goto error; - } else { - uint32_t temp = 0; - if (!WriteToRFBServer(client, (char *)&temp, 4)) goto error; - } - - rfbClientLog("%s", "Getting sever start negotiation reply\n"); - /* Read the 'START' message reply from server */ - if (!ReadFromRFBServer(client, (char *)&serverinlen, 4)) goto error; - serverinlen = rfbClientSwap32IfLE(serverinlen); - - if (serverinlen > SASL_MAX_DATA_LEN) { - rfbClientLog("SASL negotiation data too long: %d bytes\n", - serverinlen); - goto error; - } - - /* NB, distinction of NULL vs "" is *critical* in SASL */ - if (serverinlen) { - serverin = malloc(serverinlen); - if (!ReadFromRFBServer(client, serverin, serverinlen)) goto error; - serverin[serverinlen-1] = '\0'; - serverinlen--; - } else { - serverin = NULL; - } - if (!ReadFromRFBServer(client, (char *)&complete, 1)) goto error; - - rfbClientLog("Client start result complete: %d. Data %d bytes %p '%s'\n", - complete, serverinlen, serverin, serverin); - - /* Loop-the-loop... - * Even if the server has completed, the client must *always* do at least one step - * in this loop to verify the server isn't lying about something. Mutual auth */ - for (;;) { - restep: - err = sasl_client_step(saslconn, - serverin, - serverinlen, - &interact, - &clientout, - &clientoutlen); - if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) { - rfbClientLog("Failed SASL step: %d (%s)\n", - err, sasl_errdetail(saslconn)); - goto error; - } - - /* Need to gather some credentials from the client */ - if (err == SASL_INTERACT) { - rfbClientLog("User interaction required but not currently supported\n"); - goto error; - } - - if (serverin) { - free(serverin); - serverin = NULL; - } - - rfbClientLog("Client step result %d. Data %d bytes %p '%s'\n", err, clientoutlen, clientout, clientout); - - /* Previous server call showed completion & we're now locally complete too */ - if (complete && err == SASL_OK) - break; - - /* Not done, prepare to talk with the server for another iteration */ - - /* NB, distinction of NULL vs "" is *critical* in SASL */ - if (clientout) { - uint32_t colsw = rfbClientSwap32IfLE(clientoutlen + 1); - if (!WriteToRFBServer(client, (char *)&colsw, 4)) goto error; - if (!WriteToRFBServer(client, (char *)clientout, clientoutlen + 1)) goto error; - } else { - uint32_t temp = 0; - if (!WriteToRFBServer(client, (char *)&temp, 4)) goto error; - } - - rfbClientLog("Server step with %d bytes %p\n", clientoutlen, clientout); - - if (!ReadFromRFBServer(client, (char *)&serverinlen, 4)) goto error; - serverinlen = rfbClientSwap32IfLE(serverinlen); - - if (serverinlen > SASL_MAX_DATA_LEN) { - rfbClientLog("SASL negotiation data too long: %d bytes\n", - serverinlen); - goto error; - } - - /* NB, distinction of NULL vs "" is *critical* in SASL */ - if (serverinlen) { - serverin = malloc(serverinlen); - if (!ReadFromRFBServer(client, serverin, serverinlen)) goto error; - serverin[serverinlen-1] = '\0'; - serverinlen--; - } else { - serverin = NULL; - } - if (!ReadFromRFBServer(client, (char *)&complete, 1)) goto error; - - rfbClientLog("Client step result complete: %d. Data %d bytes %p '%s'\n", - complete, serverinlen, serverin, serverin); - - /* This server call shows complete, and earlier client step was OK */ - if (complete && err == SASL_OK) { - free(serverin); - serverin = NULL; - break; - } - } - - /* Check for suitable SSF if non-TLS */ - if (!client->tlsSession) { - err = sasl_getprop(saslconn, SASL_SSF, &val); - if (err != SASL_OK) { - rfbClientLog("cannot query SASL ssf on connection %d (%s)\n", - err, sasl_errstring(err, NULL, NULL)); - goto error; - } - ssf = *(const int *)val; - rfbClientLog("SASL SSF value %d\n", ssf); - if (ssf < 56) { /* 56 == DES level, good for Kerberos */ - rfbClientLog("negotiation SSF %d was not strong enough\n", ssf); - goto error; - } - } - - rfbClientLog("%s", "SASL authentication complete\n"); - - uint32_t result; - if (!ReadFromRFBServer(client, (char *)&result, 4)) { - rfbClientLog("Failed to read authentication result\n"); - goto error; - } - result = rfbClientSwap32IfLE(result); - - if (result != 0) { - rfbClientLog("Authentication failure\n"); - goto error; - } - rfbClientLog("Authentication successful - switching to SSF\n"); - - /* This must come *after* check-auth-result, because the former - * is defined to be sent unencrypted, and setting saslconn turns - * on the SSF layer encryption processing */ - client->saslconn = saslconn; - - /* Clear SASL secret from memory if set - it'll be free'd on dispose */ - if (client->saslSecret) { - size_t i; - for (i = 0; i < client->saslSecret->len; i++) - client->saslSecret->data[i] = '\0'; - client->saslSecret->len = 0; - } - - return TRUE; - - error: - if (client->saslSecret) { - size_t i; - for (i = 0; i < client->saslSecret->len; i++) - client->saslSecret->data[i] = '\0'; - client->saslSecret->len = 0; - } - - if (saslconn) - sasl_dispose(&saslconn); - return FALSE; -} - -int -ReadFromSASL(rfbClient* client, char *out, unsigned int n) -{ - size_t want; - - if (client->saslDecoded == NULL) { - char *encoded; - int encodedLen; - int err, ret; - - encodedLen = 8192; - encoded = (char *)malloc(encodedLen); - - ret = read(client->sock, encoded, encodedLen); - if (ret < 0) { - free(encoded); - return ret; - } - if (ret == 0) { - free(encoded); - errno = EIO; - return -EIO; - } - - err = sasl_decode(client->saslconn, encoded, ret, - &client->saslDecoded, &client->saslDecodedLength); - free(encoded); - if (err != SASL_OK) { - rfbClientLog("Failed to decode SASL data %s\n", - sasl_errstring(err, NULL, NULL)); - return -EINVAL; - } - client->saslDecodedOffset = 0; - } - - want = client->saslDecodedLength - client->saslDecodedOffset; - if (want > n) - want = n; - - memcpy(out, - client->saslDecoded + client->saslDecodedOffset, - want); - client->saslDecodedOffset += want; - if (client->saslDecodedOffset == client->saslDecodedLength) { - client->saslDecodedLength = client->saslDecodedOffset = 0; - client->saslDecoded = NULL; - } - - if (!want) { - errno = EAGAIN; - return -EAGAIN; - } - - return want; -} diff --git a/libvncclient/rfbsasl.h b/libvncclient/rfbsasl.h deleted file mode 100644 index 2936364..0000000 --- a/libvncclient/rfbsasl.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef RFBSASL_H -#define RFBSASL_H - -/* - * Copyright (C) 2017 S. Waterman. All Rights Reserved. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -#ifdef LIBVNCSERVER_HAVE_SASL - -#include - -/* - * Perform the SASL authentication process - */ -rfbBool HandleSASLAuth(rfbClient *client); - -/* - * Read from SASL when the SASL SSF is in use. - */ -int ReadFromSASL(rfbClient* client, char *out, unsigned int n); - -#endif /* LIBVNCSERVER_HAVE_SASL */ - -#endif /* RFBSASL_H */ diff --git a/libvncclient/sasl.c b/libvncclient/sasl.c new file mode 100644 index 0000000..0530307 --- /dev/null +++ b/libvncclient/sasl.c @@ -0,0 +1,579 @@ +/* + * The software in this file is derived from the vncconnection.c source file + * from the GTK VNC Widget with modifications by S. Waterman + * for compatibility with libvncserver. The copyright and license + * statements below apply only to this source file and to no other parts of the + * libvncserver library. + * + * GTK VNC Widget + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2009-2010 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * sasl.c - functions to deal with client side of the SASL protocol. + */ + +#ifdef __STRICT_ANSI__ +#define _BSD_SOURCE +#define _POSIX_SOURCE +#define _XOPEN_SOURCE 600 +#endif + +#include +#include + +#ifdef WIN32 +#undef SOCKET +#include +#define EWOULDBLOCK WSAEWOULDBLOCK +#define socklen_t int +#define close closesocket +#define read(sock,buf,len) recv(sock,buf,len,0) +#define write(sock,buf,len) send(sock,buf,len,0) +#ifdef LIBVNCSERVER_HAVE_WS2TCPIP_H +#undef socklen_t +#include +#endif /* LIBVNCSERVER_HAVE_WS2TCPIP_H */ +#else /* WIN32 */ +#include +#endif /* WIN32 */ + +#include "sasl.h" + +#include "tls.h" + +#ifdef _MSC_VER +# define snprintf _snprintf /* MSVC went straight to the underscored syntax */ +#endif + +/* + * NB, keep in sync with similar method in qemud/remote.c + */ +static char *vnc_connection_addr_to_string(char *host, int port) +{ + char * buf = (char *)malloc(strlen(host) + 7); + sprintf(buf, "%s;%hu", host, port); + return buf; +} + +static int log_func(void *context, + int level, + const char *message) +{ + rfbClientLog("SASL: %s\n", message); + + return SASL_OK; +} + +static int user_callback_adapt(void *context, + int id, + const char **result, + unsigned *len) +{ + rfbClient* client = (rfbClient *)context; + + if (id != SASL_CB_AUTHNAME) { + rfbClientLog("Unrecognized SASL callback ID %d\n", id); + return SASL_FAIL; + } + + if (!client->GetUser) { + rfbClientLog("Client user callback not found\n"); + return SASL_FAIL; + } + + *result = client->GetUser(client); + + if (! *result) return SASL_FAIL; + /**len = strlen(*result);*/ + return SASL_OK; +} + +static int password_callback_adapt(sasl_conn_t *conn, + void * context, + int id, + sasl_secret_t **secret) +{ + rfbClient* client = (rfbClient *)context; + char * password; + + if (id != SASL_CB_PASS) { + rfbClientLog("Unrecognized SASL callback ID %d\n", id); + return SASL_FAIL; + } + + if (client->saslSecret) { /* If we've already got it just return it. */ + *secret = client->saslSecret; + return SASL_OK; + } + + if (!client->GetPassword) { + rfbClientLog("Client password callback not found\n"); + return SASL_FAIL; + } + + password = client->GetPassword(client); + + if (! password) return SASL_FAIL; + + sasl_secret_t *lsec = (sasl_secret_t *)malloc(sizeof(sasl_secret_t) + strlen(password)); + if (!lsec) { + rfbClientLog("Could not allocate sasl_secret_t\n"); + return SASL_FAIL; + } + + strcpy(lsec->data, password); + lsec->len = strlen(password); + client->saslSecret = lsec; + *secret = lsec; + + /* Clear client password */ + size_t i; + for (i = 0; i < lsec->len; i++) { + password[i] = '\0'; + } + free(password); + + return SASL_OK; +} + +#define SASL_MAX_MECHLIST_LEN 300 +#define SASL_MAX_DATA_LEN (1024 * 1024) + +/* Perform the SASL authentication process + */ +rfbBool +HandleSASLAuth(rfbClient *client) +{ + sasl_conn_t *saslconn = NULL; + sasl_security_properties_t secprops; + const char *clientout; + char *serverin = NULL; + unsigned int clientoutlen, serverinlen; + int err, complete = 0; + char *localAddr = NULL, *remoteAddr = NULL; + const void *val; + sasl_ssf_t ssf; + sasl_callback_t saslcb[] = { + {SASL_CB_LOG, (void *)log_func, NULL}, + {SASL_CB_AUTHNAME, client->GetUser ? (void *)user_callback_adapt : NULL, client}, + {SASL_CB_PASS, client->GetPassword ? (void *)password_callback_adapt : NULL, client}, + { .id = 0 }, + }; + sasl_interact_t *interact = NULL; + uint32_t mechlistlen; + char *mechlist; + char *wantmech; + const char *mechname; + rfbBool ret; + + client->saslconn = NULL; + + /* Sets up the SASL library as a whole */ + err = sasl_client_init(NULL); + rfbClientLog("Client initialize SASL authentication %d\n", err); + if (err != SASL_OK) { + rfbClientLog("failed to initialize SASL library: %d (%s)\n", + err, sasl_errstring(err, NULL, NULL)); + goto error; + } + + /* Get local address in form IPADDR:PORT */ + struct sockaddr_storage localAddress; + socklen_t addressLength = sizeof(localAddress); + char buf[INET6_ADDRSTRLEN]; + int port; + + if (getsockname(client->sock, (struct sockaddr*)&localAddress, &addressLength)) { + rfbClientLog("failed to get local address\n"); + goto error; + } + + if (localAddress.ss_family == AF_INET) { + struct sockaddr_in *sa_in = (struct sockaddr_in*)&localAddress; + inet_ntop(AF_INET, &(sa_in->sin_addr), buf, INET_ADDRSTRLEN); + port = ntohs(sa_in->sin_port); + localAddr = vnc_connection_addr_to_string(buf, port); + } else if (localAddress.ss_family == AF_INET6) { + struct sockaddr_in6 *sa_in = (struct sockaddr_in6*)&localAddress; + inet_ntop(AF_INET6, &(sa_in->sin6_addr), buf, INET6_ADDRSTRLEN); + port = ntohs(sa_in->sin6_port); + localAddr = vnc_connection_addr_to_string(buf, port); + } else { + rfbClientLog("failed to get local address\n"); + goto error; + } + + /* Get remote address in form IPADDR:PORT */ + remoteAddr = vnc_connection_addr_to_string(client->serverHost, client->serverPort); + + rfbClientLog("Client SASL new host:'%s' local:'%s' remote:'%s'\n", client->serverHost, localAddr, remoteAddr); + + /* Setup a handle for being a client */ + err = sasl_client_new("vnc", + client->serverHost, + localAddr, + remoteAddr, + saslcb, + SASL_SUCCESS_DATA, + &saslconn); + free(localAddr); + free(remoteAddr); + + if (err != SASL_OK) { + rfbClientLog("Failed to create SASL client context: %d (%s)\n", + err, sasl_errstring(err, NULL, NULL)); + goto error; + } + + /* Initialize some connection props we care about */ + if (client->tlsSession) { + if (!(ssf = (sasl_ssf_t)GetTLSCipherBits(client))) { + rfbClientLog("%s", "invalid cipher size for TLS session\n"); + goto error; + } + + rfbClientLog("Setting external SSF %d\n", ssf); + err = sasl_setprop(saslconn, SASL_SSF_EXTERNAL, &ssf); + if (err != SASL_OK) { + rfbClientLog("cannot set external SSF %d (%s)\n", + err, sasl_errstring(err, NULL, NULL)); + goto error; + } + } + + memset (&secprops, 0, sizeof secprops); + /* If we've got TLS, we don't care about SSF */ + secprops.min_ssf = client->tlsSession ? 0 : 56; /* Equiv to DES supported by all Kerberos */ + secprops.max_ssf = client->tlsSession ? 0 : 100000; /* Very strong ! AES == 256 */ + secprops.maxbufsize = 100000; + /* If we're not TLS, then forbid any anonymous or trivially crackable auth */ + secprops.security_flags = client->tlsSession ? 0 : + SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT; + + err = sasl_setprop(saslconn, SASL_SEC_PROPS, &secprops); + if (err != SASL_OK) { + rfbClientLog("cannot set security props %d (%s)\n", + err, sasl_errstring(err, NULL, NULL)); + goto error; + } + + /* Get the supported mechanisms from the server */ + if (!ReadFromRFBServer(client, (char *)&mechlistlen, 4)) { + rfbClientLog("failed to read mechlistlen\n"); + goto error; + } + mechlistlen = rfbClientSwap32IfLE(mechlistlen); + rfbClientLog("mechlistlen is %d\n", mechlistlen); + if (mechlistlen > SASL_MAX_MECHLIST_LEN) { + rfbClientLog("mechlistlen %d too long\n", mechlistlen); + goto error; + } + + mechlist = malloc(mechlistlen+1); + if (!ReadFromRFBServer(client, mechlist, mechlistlen)) { + free(mechlist); + goto error; + } + mechlist[mechlistlen] = '\0'; + + /* Allow the client to influence the mechanism selected. */ + if (client->GetSASLMechanism) { + wantmech = client->GetSASLMechanism(client, mechlist); + + if (wantmech && *wantmech != 0) { + if (strstr(mechlist, wantmech) == NULL) { + rfbClientLog("Client requested SASL mechanism %s not supported by server\n", + wantmech); + free(mechlist); + free(wantmech); + goto error; + } else { + free(mechlist); + mechlist = wantmech; + } + } + } + + rfbClientLog("Client start negotiation mechlist '%s'\n", mechlist); + + restart: + /* Start the auth negotiation on the client end first */ + err = sasl_client_start(saslconn, + mechlist, + &interact, + &clientout, + &clientoutlen, + &mechname); + if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) { + rfbClientLog("Failed to start SASL negotiation: %d (%s)\n", + err, sasl_errdetail(saslconn)); + free(mechlist); + mechlist = NULL; + goto error; + } + + /* Need to gather some credentials from the client */ + if (err == SASL_INTERACT) { + rfbClientLog("User interaction required but not currently supported\n"); + goto error; + } + + rfbClientLog("Server start negotiation with mech %s. Data %d bytes %p '%s'\n", + mechname, clientoutlen, clientout, clientout); + + if (clientoutlen > SASL_MAX_DATA_LEN) { + rfbClientLog("SASL negotiation data too long: %d bytes\n", + clientoutlen); + goto error; + } + + /* Send back the chosen mechname */ + uint32_t mechnamelen = rfbClientSwap32IfLE(strlen(mechname)); + if (!WriteToRFBServer(client, (char *)&mechnamelen, 4)) goto error; + if (!WriteToRFBServer(client, (char *)mechname, strlen(mechname))) goto error; + + /* NB, distinction of NULL vs "" is *critical* in SASL */ + if (clientout) { + uint32_t colsw = rfbClientSwap32IfLE(clientoutlen + 1); + if (!WriteToRFBServer(client, (char *)&colsw, 4)) goto error; + if (!WriteToRFBServer(client, (char *)clientout, clientoutlen + 1)) goto error; + } else { + uint32_t temp = 0; + if (!WriteToRFBServer(client, (char *)&temp, 4)) goto error; + } + + rfbClientLog("%s", "Getting sever start negotiation reply\n"); + /* Read the 'START' message reply from server */ + if (!ReadFromRFBServer(client, (char *)&serverinlen, 4)) goto error; + serverinlen = rfbClientSwap32IfLE(serverinlen); + + if (serverinlen > SASL_MAX_DATA_LEN) { + rfbClientLog("SASL negotiation data too long: %d bytes\n", + serverinlen); + goto error; + } + + /* NB, distinction of NULL vs "" is *critical* in SASL */ + if (serverinlen) { + serverin = malloc(serverinlen); + if (!ReadFromRFBServer(client, serverin, serverinlen)) goto error; + serverin[serverinlen-1] = '\0'; + serverinlen--; + } else { + serverin = NULL; + } + if (!ReadFromRFBServer(client, (char *)&complete, 1)) goto error; + + rfbClientLog("Client start result complete: %d. Data %d bytes %p '%s'\n", + complete, serverinlen, serverin, serverin); + + /* Loop-the-loop... + * Even if the server has completed, the client must *always* do at least one step + * in this loop to verify the server isn't lying about something. Mutual auth */ + for (;;) { + restep: + err = sasl_client_step(saslconn, + serverin, + serverinlen, + &interact, + &clientout, + &clientoutlen); + if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) { + rfbClientLog("Failed SASL step: %d (%s)\n", + err, sasl_errdetail(saslconn)); + goto error; + } + + /* Need to gather some credentials from the client */ + if (err == SASL_INTERACT) { + rfbClientLog("User interaction required but not currently supported\n"); + goto error; + } + + if (serverin) { + free(serverin); + serverin = NULL; + } + + rfbClientLog("Client step result %d. Data %d bytes %p '%s'\n", err, clientoutlen, clientout, clientout); + + /* Previous server call showed completion & we're now locally complete too */ + if (complete && err == SASL_OK) + break; + + /* Not done, prepare to talk with the server for another iteration */ + + /* NB, distinction of NULL vs "" is *critical* in SASL */ + if (clientout) { + uint32_t colsw = rfbClientSwap32IfLE(clientoutlen + 1); + if (!WriteToRFBServer(client, (char *)&colsw, 4)) goto error; + if (!WriteToRFBServer(client, (char *)clientout, clientoutlen + 1)) goto error; + } else { + uint32_t temp = 0; + if (!WriteToRFBServer(client, (char *)&temp, 4)) goto error; + } + + rfbClientLog("Server step with %d bytes %p\n", clientoutlen, clientout); + + if (!ReadFromRFBServer(client, (char *)&serverinlen, 4)) goto error; + serverinlen = rfbClientSwap32IfLE(serverinlen); + + if (serverinlen > SASL_MAX_DATA_LEN) { + rfbClientLog("SASL negotiation data too long: %d bytes\n", + serverinlen); + goto error; + } + + /* NB, distinction of NULL vs "" is *critical* in SASL */ + if (serverinlen) { + serverin = malloc(serverinlen); + if (!ReadFromRFBServer(client, serverin, serverinlen)) goto error; + serverin[serverinlen-1] = '\0'; + serverinlen--; + } else { + serverin = NULL; + } + if (!ReadFromRFBServer(client, (char *)&complete, 1)) goto error; + + rfbClientLog("Client step result complete: %d. Data %d bytes %p '%s'\n", + complete, serverinlen, serverin, serverin); + + /* This server call shows complete, and earlier client step was OK */ + if (complete && err == SASL_OK) { + free(serverin); + serverin = NULL; + break; + } + } + + /* Check for suitable SSF if non-TLS */ + if (!client->tlsSession) { + err = sasl_getprop(saslconn, SASL_SSF, &val); + if (err != SASL_OK) { + rfbClientLog("cannot query SASL ssf on connection %d (%s)\n", + err, sasl_errstring(err, NULL, NULL)); + goto error; + } + ssf = *(const int *)val; + rfbClientLog("SASL SSF value %d\n", ssf); + if (ssf < 56) { /* 56 == DES level, good for Kerberos */ + rfbClientLog("negotiation SSF %d was not strong enough\n", ssf); + goto error; + } + } + + rfbClientLog("%s", "SASL authentication complete\n"); + + uint32_t result; + if (!ReadFromRFBServer(client, (char *)&result, 4)) { + rfbClientLog("Failed to read authentication result\n"); + goto error; + } + result = rfbClientSwap32IfLE(result); + + if (result != 0) { + rfbClientLog("Authentication failure\n"); + goto error; + } + rfbClientLog("Authentication successful - switching to SSF\n"); + + /* This must come *after* check-auth-result, because the former + * is defined to be sent unencrypted, and setting saslconn turns + * on the SSF layer encryption processing */ + client->saslconn = saslconn; + + /* Clear SASL secret from memory if set - it'll be free'd on dispose */ + if (client->saslSecret) { + size_t i; + for (i = 0; i < client->saslSecret->len; i++) + client->saslSecret->data[i] = '\0'; + client->saslSecret->len = 0; + } + + return TRUE; + + error: + if (client->saslSecret) { + size_t i; + for (i = 0; i < client->saslSecret->len; i++) + client->saslSecret->data[i] = '\0'; + client->saslSecret->len = 0; + } + + if (saslconn) + sasl_dispose(&saslconn); + return FALSE; +} + +int +ReadFromSASL(rfbClient* client, char *out, unsigned int n) +{ + size_t want; + + if (client->saslDecoded == NULL) { + char *encoded; + int encodedLen; + int err, ret; + + encodedLen = 8192; + encoded = (char *)malloc(encodedLen); + + ret = read(client->sock, encoded, encodedLen); + if (ret < 0) { + free(encoded); + return ret; + } + if (ret == 0) { + free(encoded); + errno = EIO; + return -EIO; + } + + err = sasl_decode(client->saslconn, encoded, ret, + &client->saslDecoded, &client->saslDecodedLength); + free(encoded); + if (err != SASL_OK) { + rfbClientLog("Failed to decode SASL data %s\n", + sasl_errstring(err, NULL, NULL)); + return -EINVAL; + } + client->saslDecodedOffset = 0; + } + + want = client->saslDecodedLength - client->saslDecodedOffset; + if (want > n) + want = n; + + memcpy(out, + client->saslDecoded + client->saslDecodedOffset, + want); + client->saslDecodedOffset += want; + if (client->saslDecodedOffset == client->saslDecodedLength) { + client->saslDecodedLength = client->saslDecodedOffset = 0; + client->saslDecoded = NULL; + } + + if (!want) { + errno = EAGAIN; + return -EAGAIN; + } + + return want; +} diff --git a/libvncclient/sasl.h b/libvncclient/sasl.h new file mode 100644 index 0000000..5a52149 --- /dev/null +++ b/libvncclient/sasl.h @@ -0,0 +1,39 @@ +#ifndef SASL_H +#define SASL_H + +/* + * Copyright (C) 2017 S. Waterman. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifdef LIBVNCSERVER_HAVE_SASL + +#include + +/* + * Perform the SASL authentication process + */ +rfbBool HandleSASLAuth(rfbClient *client); + +/* + * Read from SASL when the SASL SSF is in use. + */ +int ReadFromSASL(rfbClient* client, char *out, unsigned int n); + +#endif /* LIBVNCSERVER_HAVE_SASL */ + +#endif /* SASL_H */ diff --git a/libvncclient/sockets.c b/libvncclient/sockets.c index 2d505c7..ed2deef 100644 --- a/libvncclient/sockets.c +++ b/libvncclient/sockets.c @@ -58,7 +58,7 @@ #include #endif #include "tls.h" -#include "rfbsasl.h" +#include "sasl.h" #ifdef _MSC_VER # define snprintf _snprintf -- cgit v1.2.1 From beb82f8dde3c1e1d4d6600e568283919e5a04712 Mon Sep 17 00:00:00 2001 From: Wiki Wang Date: Fri, 15 Sep 2017 14:21:07 +0800 Subject: Add trle decoder --- libvncclient/rfbproto.c | 63 +++++++++++ libvncclient/trle.c | 296 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 359 insertions(+) create mode 100644 libvncclient/trle.c (limited to 'libvncclient') diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index df8b6d0..242fd52 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -161,6 +161,13 @@ static rfbBool HandleUltra32(rfbClient* client, int rx, int ry, int rw, int rh); static rfbBool HandleUltraZip8(rfbClient* client, int rx, int ry, int rw, int rh); static rfbBool HandleUltraZip16(rfbClient* client, int rx, int ry, int rw, int rh); static rfbBool HandleUltraZip32(rfbClient* client, int rx, int ry, int rw, int rh); +static rfbBool HandleTRLE8(rfbClient* client, int rx, int ry, int rw, int rh); +static rfbBool HandleTRLE15(rfbClient* client, int rx, int ry, int rw, int rh); +static rfbBool HandleTRLE16(rfbClient* client, int rx, int ry, int rw, int rh); +static rfbBool HandleTRLE24(rfbClient* client, int rx, int ry, int rw, int rh); +static rfbBool HandleTRLE24Up(rfbClient* client, int rx, int ry, int rw, int rh); +static rfbBool HandleTRLE24Down(rfbClient* client, int rx, int ry, int rw, int rh); +static rfbBool HandleTRLE32(rfbClient* client, int rx, int ry, int rw, int rh); #ifdef LIBVNCSERVER_HAVE_LIBZ static rfbBool HandleZlib8(rfbClient* client, int rx, int ry, int rw, int rh); static rfbBool HandleZlib16(rfbClient* client, int rx, int ry, int rw, int rh); @@ -1295,6 +1302,8 @@ SetFormatAndEncodings(rfbClient* client) encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZlibHex); if (client->appData.compressLevel >= 0 && client->appData.compressLevel <= 9) requestCompressLevel = TRUE; + } else if (strncasecmp(encStr,"trle",encStrLen) == 0) { + encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingTRLE); } else if (strncasecmp(encStr,"zrle",encStrLen) == 0) { encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZRLE); } else if (strncasecmp(encStr,"zywrle",encStrLen) == 0) { @@ -2010,6 +2019,47 @@ HandleRFBServerMessage(rfbClient* client) break; } + case rfbEncodingTRLE: + { + switch (client->format.bitsPerPixel) { + case 8: + if (!HandleTRLE8(client, rect.r.x, rect.r.y, rect.r.w, rect.r.h)) + return FALSE; + break; + case 16: + if (client->si.format.greenMax > 0x1F) { + if (!HandleTRLE16(client, rect.r.x, rect.r.y, rect.r.w, rect.r.h)) + return FALSE; + } else { + if (!HandleTRLE15(client, rect.r.x, rect.r.y, rect.r.w, rect.r.h)) + return FALSE; + } + break; + case 32: { + uint32_t maxColor = + (client->format.redMax << client->format.redShift) | + (client->format.greenMax << client->format.greenShift) | + (client->format.blueMax << client->format.blueShift); + if ((client->format.bigEndian && (maxColor & 0xff) == 0) || + (!client->format.bigEndian && (maxColor & 0xff000000) == 0)) { + if (!HandleTRLE24(client, rect.r.x, rect.r.y, rect.r.w, rect.r.h)) + return FALSE; + } else if (!client->format.bigEndian && (maxColor & 0xff) == 0) { + if (!HandleTRLE24Up(client, rect.r.x, rect.r.y, rect.r.w, rect.r.h)) + return FALSE; + } else if (client->format.bigEndian && (maxColor & 0xff000000) == 0) { + if (!HandleTRLE24Down(client, rect.r.x, rect.r.y, rect.r.w, + rect.r.h)) + return FALSE; + } else if (!HandleTRLE32(client, rect.r.x, rect.r.y, rect.r.w, + rect.r.h)) + return FALSE; + break; + } + } + break; + } + #ifdef LIBVNCSERVER_HAVE_LIBZ case rfbEncodingZlib: { @@ -2300,6 +2350,7 @@ HandleRFBServerMessage(rfbClient* client) #include "ultra.c" #include "zlib.c" #include "tight.c" +#include "trle.c" #include "zrle.c" #undef BPP #define BPP 16 @@ -2309,8 +2360,11 @@ HandleRFBServerMessage(rfbClient* client) #include "ultra.c" #include "zlib.c" #include "tight.c" +#include "trle.c" #include "zrle.c" #define REALBPP 15 +#include "trle.c" +#define REALBPP 15 #include "zrle.c" #undef BPP #define BPP 32 @@ -2320,14 +2374,23 @@ HandleRFBServerMessage(rfbClient* client) #include "ultra.c" #include "zlib.c" #include "tight.c" +#include "trle.c" #include "zrle.c" #define REALBPP 24 +#include "trle.c" +#define REALBPP 24 #include "zrle.c" #define REALBPP 24 #define UNCOMP 8 +#include "trle.c" +#define REALBPP 24 +#define UNCOMP 8 #include "zrle.c" #define REALBPP 24 #define UNCOMP -8 +#include "trle.c" +#define REALBPP 24 +#define UNCOMP -8 #include "zrle.c" #undef BPP diff --git a/libvncclient/trle.c b/libvncclient/trle.c new file mode 100644 index 0000000..b8d6e5c --- /dev/null +++ b/libvncclient/trle.c @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2017 Wiki Wang . All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * trle.c - handle trle encoding. + * + * This file shouldn't be compiled directly. It is included multiple times by + * rfbproto.c, each time with a different definition of the macro BPP. For + * each value of BPP, this file defines a function which handles a trle + * encoded rectangle with BPP bits per pixel. + */ + +#ifndef REALBPP +#define REALBPP BPP +#endif + +#if !defined(UNCOMP) || UNCOMP == 0 +#define HandleTRLE CONCAT2E(HandleTRLE, REALBPP) +#elif UNCOMP > 0 +#define HandleTRLE CONCAT3E(HandleTRLE, REALBPP, Down) +#else +#define HandleTRLE CONCAT3E(HandleTRLE, REALBPP, Up) +#endif +#define CARDBPP CONCAT3E(uint, BPP, _t) +#define CARDREALBPP CONCAT3E(uint, REALBPP, _t) + +#if REALBPP != BPP && defined(UNCOMP) && UNCOMP != 0 +#if UNCOMP > 0 +#define UncompressCPixel(pointer) ((*(CARDBPP *)pointer) >> UNCOMP) +#else +#define UncompressCPixel(pointer) ((*(CARDBPP *)pointer) << (-(UNCOMP))) +#endif +#else +#define UncompressCPixel(pointer) (*(CARDBPP *)pointer) +#endif + +static rfbBool HandleTRLE(rfbClient *client, int rx, int ry, int rw, int rh) { + int x, y, w, h; + uint8_t type, last_type; + int min_buffer_size = 16 * 16 * (REALBPP / 8) * 2; + uint8_t *buffer; + CARDBPP palette[128]; + int bpp, mask, divider; + CARDBPP color; + + /* First make sure we have a large enough raw buffer to hold the + * decompressed data. In practice, with a fixed REALBPP, fixed frame + * buffer size and the first update containing the entire frame + * buffer, this buffer allocation should only happen once, on the + * first update. + */ + if (client->raw_buffer_size < min_buffer_size) { + + if (client->raw_buffer != NULL) { + + free(client->raw_buffer); + } + + client->raw_buffer_size = min_buffer_size; + client->raw_buffer = (char *)malloc(client->raw_buffer_size); + } + + rfbClientLog("Update %d %d %d %d\n", rx, ry, rw, rh); + + for (y = ry; y < ry + rh; y += 16) { + for (x = rx; x < rx + rw; x += 16) { + w = h = 16; + if (rx + rw - x < 16) + w = rx + rw - x; + if (ry + rh - y < 16) + h = ry + rh - y; + + if (!ReadFromRFBServer(client, &type, 1)) + return FALSE; + + buffer = client->raw_buffer; + + switch (type) { + case_0: + case 0: { + if (!ReadFromRFBServer(client, buffer, w * h * REALBPP / 8)) + return FALSE; +#if REALBPP != BPP + int i, j; + + for (j = y * client->width; j < (y + h) * client->width; + j += client->width) + for (i = x; i < x + w; i++, buffer += REALBPP / 8) + ((CARDBPP *)client->frameBuffer)[j + i] = UncompressCPixel(buffer); +#else + client->GotBitmap(client, buffer, x, y, w, h); +#endif + type = last_type; + break; + } + case 1: { + if (!ReadFromRFBServer(client, buffer, REALBPP / 8)) + return FALSE; + + color = UncompressCPixel(buffer); + + client->GotFillRect(client, x, y, w, h, color); + + last_type = type; + break; + } + case_127: + case 127: + switch (last_type) { + case 0: + return FALSE; + case 1: + client->GotFillRect(client, x, y, w, h, color); + type = last_type; + break; + case 128: + return FALSE; + default: + if (last_type >= 130) { + last_type = last_type & 0x7f; + + bpp = (last_type > 4 ? (last_type > 16 ? 8 : 4) + : (last_type > 2 ? 2 : 1)), + mask = (1 << bpp) - 1, divider = (8 / bpp); + } + if (last_type <= 16) { + int i, j, shift; + + if (!ReadFromRFBServer(client, buffer, + (w + divider - 1) / divider * h)) + return FALSE; + + /* read palettized pixels */ + for (j = y * client->width; j < (y + h) * client->width; + j += client->width) { + for (i = x, shift = 8 - bpp; i < x + w; i++) { + ((CARDBPP *)client->frameBuffer)[j + i] = + palette[((*buffer) >> shift) & mask]; + shift -= bpp; + if (shift < 0) { + shift = 8 - bpp; + buffer++; + } + } + if (shift < 8 - bpp) + buffer++; + + type = last_type; + } + } else + return FALSE; + } + break; + case 128: { + int i = 0, j = 0; + while (j < h) { + int color, length; + /* read color */ + if (!ReadFromRFBServer(client, buffer, REALBPP / 8 + 1)) + return FALSE; + color = UncompressCPixel(buffer); + buffer += REALBPP / 8; + /* read run length */ + length = 1; + while (*buffer == 0xff) { + if (!ReadFromRFBServer(client, buffer + 1, 1)) + return FALSE; + length += *buffer; + buffer++; + } + length += *buffer; + buffer++; + while (j < h && length > 0) { + ((CARDBPP *)client->frameBuffer)[(y + j) * client->width + x + i] = + color; + length--; + i++; + if (i >= w) { + i = 0; + j++; + } + } + if (length > 0) + rfbClientLog("Warning: possible TRLE corruption\n"); + } + + type = last_type; + + break; + } + case_129: + case 129: { + int i, j; + /* read palettized pixels */ + i = j = 0; + while (j < h) { + int color, length; + /* read color */ + if (!ReadFromRFBServer(client, buffer, 1)) + return FALSE; + color = palette[(*buffer) & 0x7f]; + length = 1; + if (*buffer & 0x80) { + if (!ReadFromRFBServer(client, buffer + 1, 1)) + return FALSE; + buffer++; + /* read run length */ + while (*buffer == 0xff) { + if (!ReadFromRFBServer(client, buffer + 1, 1)) + return FALSE; + length += *buffer; + buffer++; + } + length += *buffer; + } + buffer++; + while (j < h && length > 0) { + ((CARDBPP *)client->frameBuffer)[(y + j) * client->width + x + i] = + color; + length--; + i++; + if (i >= w) { + i = 0; + j++; + } + } + if (length > 0) + rfbClientLog("Warning: possible TRLE corruption\n"); + } + + if (type == 129) { + type = last_type; + } + + break; + } + default: + if (type <= 16) { + int i; + + bpp = (type > 4 ? (type > 16 ? 8 : 4) : (type > 2 ? 2 : 1)), + mask = (1 << bpp) - 1, divider = (8 / bpp); + + if (!ReadFromRFBServer(client, buffer, type * REALBPP / 8)) + return FALSE; + + /* read palette */ + for (i = 0; i < type; i++, buffer += REALBPP / 8) + palette[i] = UncompressCPixel(buffer); + + last_type = type; + goto case_127; + } else if (type >= 130) { + int i; + + if (!ReadFromRFBServer(client, buffer, (type - 128) * REALBPP / 8)) + return FALSE; + + /* read palette */ + for (i = 0; i < type - 128; i++, buffer += REALBPP / 8) + palette[i] = UncompressCPixel(buffer); + + last_type = type; + goto case_129; + } else + return FALSE; + } + last_type = type; + } + } + + return TRUE; +} + +#undef CARDBPP +#undef CARDREALBPP +#undef HandleTRLE +#undef UncompressCPixel +#undef REALBPP +#undef UNCOMP -- cgit v1.2.1 From d7b14624cbb9ed7b9df3532658e1edba8da606a6 Mon Sep 17 00:00:00 2001 From: DRC Date: Mon, 6 Nov 2017 17:25:41 -0600 Subject: Include Tight decoding optimizations from TurboVNC - As with the encoder, the decoder now uses the TurboJPEG wrapper, which allows it to decode JPEG images directly into the framebuffer. This eliminates a buffer copy (CopyRectangle()) as well as the expensive RGB pixel conversion in DecompressJpegRectBPP(). The TurboJPEG wrapper performs RGB pixel conversion more optimally, and only when necessary (it uses the libjpeg-turbo colorspace extensions when available, in order to avoid RGB conversion.) - The other Tight subencoding types are also now decoded directly into the framebuffer, which eliminates buffer copies. - The Tight decoder now supports the rfbTightNoZlib extension, which allows the server to bypass zlib compression when Compression Level 0 is selected. The encoder already supports this extension. Passing the data stream through zlib when Compression Level 0 is selected needlessly wastes CPU time, since all zlib is doing is copying the data internally into its own structures. --- libvncclient/rfbproto.c | 13 --- libvncclient/tight.c | 231 +++++++++++++++++++++-------------------------- libvncclient/vncviewer.c | 4 - 3 files changed, 101 insertions(+), 147 deletions(-) (limited to 'libvncclient') diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index df8b6d0..9cd23b8 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -47,12 +47,6 @@ #define Z_NULL NULL #endif #endif -#ifdef LIBVNCSERVER_HAVE_LIBJPEG -#ifdef _RPCNDR_H /* This Windows header typedefs 'boolean', jpeglib has to know */ -#define HAVE_BOOLEAN -#endif -#include -#endif #ifndef _MSC_VER /* Strings.h is not available in MSVC */ @@ -171,13 +165,6 @@ static rfbBool HandleTight16(rfbClient* client, int rx, int ry, int rw, int rh); static rfbBool HandleTight32(rfbClient* client, int rx, int ry, int rw, int rh); static long ReadCompactLen (rfbClient* client); - -static void JpegInitSource(j_decompress_ptr cinfo); -static boolean JpegFillInputBuffer(j_decompress_ptr cinfo); -static void JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes); -static void JpegTermSource(j_decompress_ptr cinfo); -static void JpegSetSrcManager(j_decompress_ptr cinfo, uint8_t *compressedData, - int compressedLen); #endif static rfbBool HandleZRLE8(rfbClient* client, int rx, int ry, int rw, int rh); static rfbBool HandleZRLE15(rfbClient* client, int rx, int ry, int rw, int rh); diff --git a/libvncclient/tight.c b/libvncclient/tight.c index 2447ad8..64883e5 100644 --- a/libvncclient/tight.c +++ b/libvncclient/tight.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2017 D. R. Commander. All Rights Reserved. + * Copyright (C) 2004-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (C) 2004 Landmark Graphics Corporation. All Rights Reserved. * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. * * This is free software; you can redistribute it and/or modify @@ -71,16 +74,16 @@ /* Type declarations */ -typedef void (*filterPtrBPP)(rfbClient* client, int, CARDBPP *); +typedef void (*filterPtrBPP)(rfbClient* client, int, int, int); /* Prototypes */ static int InitFilterCopyBPP (rfbClient* client, int rw, int rh); static int InitFilterPaletteBPP (rfbClient* client, int rw, int rh); static int InitFilterGradientBPP (rfbClient* client, int rw, int rh); -static void FilterCopyBPP (rfbClient* client, int numRows, CARDBPP *destBuffer); -static void FilterPaletteBPP (rfbClient* client, int numRows, CARDBPP *destBuffer); -static void FilterGradientBPP (rfbClient* client, int numRows, CARDBPP *destBuffer); +static void FilterCopyBPP (rfbClient* client, int srcx, int srcy, int numRows); +static void FilterPaletteBPP (rfbClient* client, int srcx, int srcy, int numRows); +static void FilterGradientBPP (rfbClient* client, int srcx, int srcy, int numRows); #if BPP != 8 static rfbBool DecompressJpegRectBPP(rfbClient* client, int x, int y, int w, int h); @@ -96,9 +99,17 @@ HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh) uint8_t filter_id; filterPtrBPP filterFn; z_streamp zs; - char *buffer2; int err, stream_id, compressedLen, bitsPixel; int bufferSize, rowSize, numRows, portionLen, rowsProcessed, extraBytes; + rfbBool readUncompressed = FALSE; + + if (client->frameBuffer == NULL) + return FALSE; + + if (rx + rw > client->width || ry + rh > client->height) { + rfbClientLog("Rect out of bounds: %dx%d at (%d, %d)\n", rx, ry, rw, rh); + return FALSE; + } if (!ReadFromRFBServer(client, (char *)&comp_ctl, 1)) return FALSE; @@ -114,6 +125,11 @@ HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh) comp_ctl >>= 1; } + if ((comp_ctl & rfbTightNoZlib) == rfbTightNoZlib) { + comp_ctl &= ~(rfbTightNoZlib); + readUncompressed = TRUE; + } + /* Handle solid rectangles. */ if (comp_ctl == rfbTightFill) { #if BPP == 32 @@ -195,10 +211,7 @@ HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh) if (!ReadFromRFBServer(client, (char*)client->buffer, rh * rowSize)) return FALSE; - buffer2 = &client->buffer[TIGHT_MIN_TO_COMPRESS * 4]; - filterFn(client, rh, (CARDBPP *)buffer2); - - client->GotBitmap(client, (uint8_t *)buffer2, rx, ry, rw, rh); + filterFn(client, rx, ry, rh); return TRUE; } @@ -209,6 +222,14 @@ HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh) rfbClientLog("Incorrect data received from the server.\n"); return FALSE; } + if (readUncompressed) { + if (!ReadFromRFBServer(client, (char*)client->buffer, compressedLen)) + return FALSE; + + filterFn(client, rx, ry, rh); + + return TRUE; + } /* Now let's initialize compression stream if needed. */ stream_id = comp_ctl & 0x03; @@ -229,7 +250,6 @@ HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh) /* Read, decode and draw actual pixel data in a loop. */ bufferSize = RFB_BUFFER_SIZE * bitsPixel / (bitsPixel + BPP) & 0xFFFFFFFC; - buffer2 = &client->buffer[bufferSize]; if (rowSize > bufferSize) { /* Should be impossible when RFB_BUFFER_SIZE >= 16384 */ rfbClientLog("Internal error: incorrect buffer size.\n"); @@ -271,14 +291,12 @@ HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh) numRows = (bufferSize - zs->avail_out) / rowSize; - filterFn(client, numRows, (CARDBPP *)buffer2); + filterFn(client, rx, ry+rowsProcessed, numRows); extraBytes = bufferSize - zs->avail_out - numRows * rowSize; if (extraBytes > 0) memcpy(client->buffer, &client->buffer[numRows * rowSize], extraBytes); - client->GotBitmap(client, (uint8_t *)buffer2, rx, ry+rowsProcessed, rw, numRows); - rowsProcessed += numRows; } while (zs->avail_out == 0); @@ -317,16 +335,19 @@ InitFilterCopyBPP (rfbClient* client, int rw, int rh) } static void -FilterCopyBPP (rfbClient* client, int numRows, CARDBPP *dst) +FilterCopyBPP (rfbClient* client, int srcx, int srcy, int numRows) { + CARDBPP *dst = + (CARDBPP *)&client->frameBuffer[(srcy * client->width + srcx) * BPP / 8]; + int y; #if BPP == 32 - int x, y; + int x; if (client->cutZeros) { for (y = 0; y < numRows; y++) { for (x = 0; x < client->rectWidth; x++) { - dst[y*client->rectWidth+x] = + dst[y*client->width+x] = RGB24_TO_PIXEL32(client->buffer[(y*client->rectWidth+x)*3], client->buffer[(y*client->rectWidth+x)*3+1], client->buffer[(y*client->rectWidth+x)*3+2]); @@ -336,7 +357,9 @@ FilterCopyBPP (rfbClient* client, int numRows, CARDBPP *dst) } #endif - memcpy (dst, client->buffer, numRows * client->rectWidth * (BPP / 8)); + for (y = 0; y < numRows; y++) + memcpy (&dst[y*client->width], &client->buffer[y*client->rectWidth], + client->rectWidth * (BPP / 8)); } static int @@ -356,8 +379,10 @@ InitFilterGradientBPP (rfbClient* client, int rw, int rh) #if BPP == 32 static void -FilterGradient24 (rfbClient* client, int numRows, uint32_t *dst) +FilterGradient24 (rfbClient* client, int srcx, int srcy, int numRows) { + CARDBPP *dst = + (CARDBPP *)&client->frameBuffer[(srcy * client->width + srcx) * BPP / 8]; int x, y, c; uint8_t thisRow[2048*3]; uint8_t pix[3]; @@ -370,7 +395,7 @@ FilterGradient24 (rfbClient* client, int numRows, uint32_t *dst) pix[c] = client->tightPrevRow[c] + client->buffer[y*client->rectWidth*3+c]; thisRow[c] = pix[c]; } - dst[y*client->rectWidth] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]); + dst[y*client->width] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]); /* Remaining pixels of a row */ for (x = 1; x < client->rectWidth; x++) { @@ -385,7 +410,7 @@ FilterGradient24 (rfbClient* client, int numRows, uint32_t *dst) pix[c] = (uint8_t)est[c] + client->buffer[(y*client->rectWidth+x)*3+c]; thisRow[x*3+c] = pix[c]; } - dst[y*client->rectWidth+x] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]); + dst[y*client->width+x] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]); } memcpy(client->tightPrevRow, thisRow, client->rectWidth * 3); @@ -395,8 +420,10 @@ FilterGradient24 (rfbClient* client, int numRows, uint32_t *dst) #endif static void -FilterGradientBPP (rfbClient* client, int numRows, CARDBPP *dst) +FilterGradientBPP (rfbClient* client, int srcx, int srcy, int numRows) { + CARDBPP *dst = + (CARDBPP *)&client->frameBuffer[(srcy * client->width + srcx) * BPP / 8]; int x, y, c; CARDBPP *src = (CARDBPP *)client->buffer; uint16_t *thatRow = (uint16_t *)client->tightPrevRow; @@ -408,7 +435,7 @@ FilterGradientBPP (rfbClient* client, int numRows, CARDBPP *dst) #if BPP == 32 if (client->cutZeros) { - FilterGradient24(client, numRows, dst); + FilterGradient24(client, srcx, srcy, numRows); return; } #endif @@ -428,7 +455,7 @@ FilterGradientBPP (rfbClient* client, int numRows, CARDBPP *dst) pix[c] = (uint16_t)(((src[y*client->rectWidth] >> shift[c]) + thatRow[c]) & max[c]); thisRow[c] = pix[c]; } - dst[y*client->rectWidth] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]); + dst[y*client->width] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]); /* Remaining pixels of a row */ for (x = 1; x < client->rectWidth; x++) { @@ -442,7 +469,7 @@ FilterGradientBPP (rfbClient* client, int numRows, CARDBPP *dst) pix[c] = (uint16_t)(((src[y*client->rectWidth+x] >> shift[c]) + est[c]) & max[c]); thisRow[x*3+c] = pix[c]; } - dst[y*client->rectWidth+x] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]); + dst[y*client->width+x] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]); } memcpy(thatRow, thisRow, client->rectWidth * 3 * sizeof(uint16_t)); } @@ -487,9 +514,11 @@ InitFilterPaletteBPP (rfbClient* client, int rw, int rh) } static void -FilterPaletteBPP (rfbClient* client, int numRows, CARDBPP *dst) +FilterPaletteBPP (rfbClient* client, int srcx, int srcy, int numRows) { int x, y, b, w; + CARDBPP *dst = + (CARDBPP *)&client->frameBuffer[(srcy * client->width + srcx) * BPP / 8]; uint8_t *src = (uint8_t *)client->buffer; CARDBPP *palette = (CARDBPP *)client->tightPalette; @@ -498,16 +527,16 @@ FilterPaletteBPP (rfbClient* client, int numRows, CARDBPP *dst) for (y = 0; y < numRows; y++) { for (x = 0; x < client->rectWidth / 8; x++) { for (b = 7; b >= 0; b--) - dst[y*client->rectWidth+x*8+7-b] = palette[src[y*w+x] >> b & 1]; + dst[y*client->width+x*8+7-b] = palette[src[y*w+x] >> b & 1]; } for (b = 7; b >= 8 - client->rectWidth % 8; b--) { - dst[y*client->rectWidth+x*8+7-b] = palette[src[y*w+x] >> b & 1]; + dst[y*client->width+x*8+7-b] = palette[src[y*w+x] >> b & 1]; } } } else { for (y = 0; y < numRows; y++) for (x = 0; x < client->rectWidth; x++) - dst[y*client->rectWidth+x] = palette[(int)src[y*client->rectWidth+x]]; + dst[y*client->width+x] = palette[(int)src[y*client->rectWidth+x]]; } } @@ -522,13 +551,9 @@ FilterPaletteBPP (rfbClient* client, int numRows, CARDBPP *dst) static rfbBool DecompressJpegRectBPP(rfbClient* client, int x, int y, int w, int h) { - struct jpeg_decompress_struct cinfo; - struct jpeg_error_mgr jerr; int compressedLen; - uint8_t *compressedData; - CARDBPP *pixelPtr; - JSAMPROW rowPointer[1]; - int dx, dy; + uint8_t *compressedData, *dst; + int pixelSize, pitch, flags = 0; compressedLen = (int)ReadCompactLen(client); if (compressedLen <= 0) { @@ -550,47 +575,57 @@ DecompressJpegRectBPP(rfbClient* client, int x, int y, int w, int h) if(client->GotJpeg != NULL) return client->GotJpeg(client, compressedData, compressedLen, x, y, w, h); - cinfo.err = jpeg_std_error(&jerr); - cinfo.client_data = client; - jpeg_create_decompress(&cinfo); - - JpegSetSrcManager(&cinfo, compressedData, compressedLen); + if (!client->tjhnd) { + if ((client->tjhnd = tjInitDecompress()) == NULL) { + rfbClientLog("TurboJPEG error: %s\n", tjGetErrorStr()); + free(compressedData); + return FALSE; + } + } - jpeg_read_header(&cinfo, TRUE); - cinfo.out_color_space = JCS_RGB; +#if BPP == 16 + flags = 0; + pixelSize = 3; + pitch = w * pixelSize; + dst = (uint8_t *)client->buffer; +#else + if (client->format.bigEndian) flags |= TJ_ALPHAFIRST; + if (client->format.redShift == 16 && client->format.blueShift == 0) + flags |= TJ_BGR; + if (client->format.bigEndian) flags ^= TJ_BGR; + pixelSize = BPP / 8; + pitch = client->width * pixelSize; + dst = &client->frameBuffer[y * pitch + x * pixelSize]; +#endif - jpeg_start_decompress(&cinfo); - if (cinfo.output_width != w || cinfo.output_height != h || - cinfo.output_components != 3) { - rfbClientLog("Tight Encoding: Wrong JPEG data received.\n"); - jpeg_destroy_decompress(&cinfo); + if (tjDecompress(client->tjhnd, compressedData, (unsigned long)compressedLen, + dst, w, pitch, h, pixelSize, flags)==-1) { + rfbClientLog("TurboJPEG error: %s\n", tjGetErrorStr()); free(compressedData); return FALSE; } - rowPointer[0] = (JSAMPROW)client->buffer; - dy = 0; - while (cinfo.output_scanline < cinfo.output_height) { - jpeg_read_scanlines(&cinfo, rowPointer, 1); - if (client->jpegError) { - break; - } - pixelPtr = (CARDBPP *)&client->buffer[RFB_BUFFER_SIZE / 2]; - for (dx = 0; dx < w; dx++) { - *pixelPtr++ = - RGB24_TO_PIXEL(BPP, client->buffer[dx*3], client->buffer[dx*3+1], client->buffer[dx*3+2]); + free(compressedData); + +#if BPP == 16 + pixelSize = BPP / 8; + pitch = client->width * pixelSize; + dst = &client->frameBuffer[y * pitch + x * pixelSize]; + { + CARDBPP *dst16=(CARDBPP *)dst, *dst2; + char *src = client->buffer; + int i, j; + + for (j = 0; j < h; j++) { + for (i = 0, dst2 = dst16; i < w; i++, dst2++, src += 3) { + *dst2 = RGB24_TO_PIXEL(BPP, src[0], src[1], src[2]); + } + dst16 += client->width; } - client->GotBitmap(client, (uint8_t *)&client->buffer[RFB_BUFFER_SIZE / 2], x, y + dy, w, 1); - dy++; } +#endif - if (!client->jpegError) - jpeg_finish_decompress(&cinfo); - - jpeg_destroy_decompress(&cinfo); - free(compressedData); - - return !client->jpegError; + return TRUE; } #else @@ -617,70 +652,6 @@ ReadCompactLen (rfbClient* client) return len; } -/* - * JPEG source manager functions for JPEG decompression in Tight decoder. - */ - -static void -JpegInitSource(j_decompress_ptr cinfo) -{ - rfbClient* client=(rfbClient*)cinfo->client_data; - client->jpegError = FALSE; -} - -static boolean -JpegFillInputBuffer(j_decompress_ptr cinfo) -{ - rfbClient* client=(rfbClient*)cinfo->client_data; - client->jpegError = TRUE; - client->jpegSrcManager->bytes_in_buffer = client->jpegBufferLen; - client->jpegSrcManager->next_input_byte = (JOCTET *)client->jpegBufferPtr; - - return TRUE; -} - -static void -JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes) -{ - rfbClient* client=(rfbClient*)cinfo->client_data; - if (num_bytes < 0 || num_bytes > client->jpegSrcManager->bytes_in_buffer) { - client->jpegError = TRUE; - client->jpegSrcManager->bytes_in_buffer = client->jpegBufferLen; - client->jpegSrcManager->next_input_byte = (JOCTET *)client->jpegBufferPtr; - } else { - client->jpegSrcManager->next_input_byte += (size_t) num_bytes; - client->jpegSrcManager->bytes_in_buffer -= (size_t) num_bytes; - } -} - -static void -JpegTermSource(j_decompress_ptr cinfo) -{ - /* nothing to do here. */ -} - -static void -JpegSetSrcManager(j_decompress_ptr cinfo, - uint8_t *compressedData, - int compressedLen) -{ - rfbClient* client=(rfbClient*)cinfo->client_data; - client->jpegBufferPtr = compressedData; - client->jpegBufferLen = (size_t)compressedLen; - - if(client->jpegSrcManager == NULL) - client->jpegSrcManager = malloc(sizeof(struct jpeg_source_mgr)); - client->jpegSrcManager->init_source = JpegInitSource; - client->jpegSrcManager->fill_input_buffer = JpegFillInputBuffer; - client->jpegSrcManager->skip_input_data = JpegSkipInputData; - client->jpegSrcManager->resync_to_restart = jpeg_resync_to_restart; - client->jpegSrcManager->term_source = JpegTermSource; - client->jpegSrcManager->next_input_byte = (JOCTET*)client->jpegBufferPtr; - client->jpegSrcManager->bytes_in_buffer = client->jpegBufferLen; - - cinfo->src = client->jpegSrcManager; -} - #endif #undef CARDBPP diff --git a/libvncclient/vncviewer.c b/libvncclient/vncviewer.c index 407b4a5..2a13f0e 100644 --- a/libvncclient/vncviewer.c +++ b/libvncclient/vncviewer.c @@ -319,7 +319,6 @@ rfbClient* rfbGetClient(int bitsPerSample,int samplesPerPixel, #ifdef LIBVNCSERVER_HAVE_LIBJPEG memset(client->zlibStreamActive,0,sizeof(rfbBool)*4); - client->jpegSrcManager = NULL; #endif #endif @@ -517,9 +516,6 @@ void rfbClientCleanup(rfbClient* client) { client->decompStream.msg != NULL) rfbClientLog("inflateEnd: %s\n", client->decompStream.msg ); } - - if (client->jpegSrcManager) - free(client->jpegSrcManager); #endif #endif -- cgit v1.2.1 From c5b8763b61924be2351bc6b2df504cc151d5f235 Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Sat, 27 Jan 2018 09:40:01 +1100 Subject: Moved undef of REALBPP down --- libvncclient/zrle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libvncclient') diff --git a/libvncclient/zrle.c b/libvncclient/zrle.c index e732046..ceba15a 100644 --- a/libvncclient/zrle.c +++ b/libvncclient/zrle.c @@ -420,8 +420,8 @@ static int HandleZRLETile(rfbClient* client, #undef HandleZRLE #undef HandleZRLETile #undef UncompressCPixel -#undef REALBPP #endif #undef UNCOMP +#undef REALBPP -- cgit v1.2.1 From 078590786cfc477785729620e4bc5585e13b22f7 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sat, 24 Mar 2018 01:54:52 +0100 Subject: libvncclient/sasl: prefix the header guard (again) to fix a warning --- libvncclient/sasl.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'libvncclient') diff --git a/libvncclient/sasl.h b/libvncclient/sasl.h index 5a52149..2936364 100644 --- a/libvncclient/sasl.h +++ b/libvncclient/sasl.h @@ -1,5 +1,5 @@ -#ifndef SASL_H -#define SASL_H +#ifndef RFBSASL_H +#define RFBSASL_H /* * Copyright (C) 2017 S. Waterman. All Rights Reserved. @@ -36,4 +36,4 @@ int ReadFromSASL(rfbClient* client, char *out, unsigned int n); #endif /* LIBVNCSERVER_HAVE_SASL */ -#endif /* SASL_H */ +#endif /* RFBSASL_H */ -- cgit v1.2.1 From e611616723035d2345cf27e43c8b2eef958d5696 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Thu, 27 Sep 2018 20:37:06 +0200 Subject: Remove the turbojpeg.h dependency from public headers Closes #230 --- libvncclient/tight.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'libvncclient') diff --git a/libvncclient/tight.c b/libvncclient/tight.c index 64883e5..df01812 100644 --- a/libvncclient/tight.c +++ b/libvncclient/tight.c @@ -23,6 +23,8 @@ #ifdef LIBVNCSERVER_HAVE_LIBZ #ifdef LIBVNCSERVER_HAVE_LIBJPEG +#include "turbojpeg.h" + /* * tight.c - handle ``tight'' encoding. * -- cgit v1.2.1 From 8b06f835e259652b0ff026898014fc7297ade858 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sat, 29 Sep 2018 20:55:24 +0200 Subject: When connecting to a repeater, only send initialised string Closes #253 --- libvncclient/rfbproto.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'libvncclient') diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index e5373bc..669e388 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -363,6 +363,7 @@ rfbBool ConnectToRFBRepeater(rfbClient* client,const char *repeaterHost, int rep rfbProtocolVersionMsg pv; int major,minor; char tmphost[250]; + int tmphostlen; #ifdef LIBVNCSERVER_IPv6 client->sock = ConnectClientToTcpAddr6(repeaterHost, repeaterPort); @@ -398,8 +399,11 @@ rfbBool ConnectToRFBRepeater(rfbClient* client,const char *repeaterHost, int rep rfbClientLog("Connected to VNC repeater, using protocol version %d.%d\n", major, minor); - snprintf(tmphost, sizeof(tmphost), "%s:%d", destHost, destPort); - if (!WriteToRFBServer(client, tmphost, sizeof(tmphost))) + tmphostlen = snprintf(tmphost, sizeof(tmphost), "%s:%d", destHost, destPort); + if(tmphostlen < 0 || tmphostlen >= (int)sizeof(tmphost)) + return FALSE; /* snprintf error or output truncated */ + + if (!WriteToRFBServer(client, tmphost, tmphostlen + 1)) return FALSE; return TRUE; -- cgit v1.2.1 From 2f5b2ad1c6c99b1ac6482c95844a84d66bb52838 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sat, 29 Sep 2018 21:10:32 +0200 Subject: LibVNCClient: don't leak uninitialised memory to remote The pad fields of the rfbClientCutTextMsg and rfbKeyEventMsg could contain arbitray memory belonging to the process, don't leak this to the remote. Closes #252 --- libvncclient/rfbproto.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'libvncclient') diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index 669e388..808ad4d 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -1643,6 +1643,7 @@ SendKeyEvent(rfbClient* client, uint32_t key, rfbBool down) if (!SupportsClient2Server(client, rfbKeyEvent)) return TRUE; + memset(&ke, 0, sizeof(ke)); ke.type = rfbKeyEvent; ke.down = down ? 1 : 0; ke.key = rfbClientSwap32IfLE(key); @@ -1661,6 +1662,7 @@ SendClientCutText(rfbClient* client, char *str, int len) if (!SupportsClient2Server(client, rfbClientCutText)) return TRUE; + memset(&cct, 0, sizeof(cct)); cct.type = rfbClientCutText; cct.length = rfbClientSwap32IfLE(len); return (WriteToRFBServer(client, (char *)&cct, sz_rfbClientCutTextMsg) && -- cgit v1.2.1 From c3115350eb8bb635d0fdb4dbbb0d0541f38ed19c Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sat, 29 Sep 2018 21:32:59 +0200 Subject: LibVNCClient: fix possible infinite loop Closes #251 --- libvncclient/rfbproto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libvncclient') diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index 808ad4d..8d6a4c1 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -1879,7 +1879,7 @@ HandleRFBServerMessage(rfbClient* client) /* Regardless of cause, do not divide by zero. */ linesToRead = bytesPerLine ? (RFB_BUFFER_SIZE / bytesPerLine) : 0; - while (h > 0) { + while (linesToRead && h > 0) { if (linesToRead > h) linesToRead = h; -- cgit v1.2.1 From 09f2f3fb6a5a163e453e5c2979054670c39694bc Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sat, 29 Sep 2018 22:07:27 +0200 Subject: LibVNCClient: make sure ReadFromRFBServer() does not write after buffer end in CoRRE decoding Closes #250 --- libvncclient/corre.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libvncclient') diff --git a/libvncclient/corre.c b/libvncclient/corre.c index 66e3b08..55107b1 100644 --- a/libvncclient/corre.c +++ b/libvncclient/corre.c @@ -48,7 +48,7 @@ HandleCoRREBPP (rfbClient* client, int rx, int ry, int rw, int rh) client->GotFillRect(client, rx, ry, rw, rh, pix); - if (!ReadFromRFBServer(client, client->buffer, hdr.nSubrects * (4 + (BPP / 8)))) + if (hdr.nSubrects * (4 + (BPP / 8)) > RFB_BUFFER_SIZE || !ReadFromRFBServer(client, client->buffer, hdr.nSubrects * (4 + (BPP / 8)))) return FALSE; ptr = (uint8_t *)client->buffer; -- cgit v1.2.1 From a83439b9fbe0f03c48eb94ed05729cb016f8b72f Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sat, 29 Sep 2018 22:28:57 +0200 Subject: LibVNCClient: fix three possible heap buffer overflows An attacker could feed `0xffffffff`, causing a `malloc(0)` for the buffers which are subsequently written to. Closes #247 --- libvncclient/rfbproto.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'libvncclient') diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index 8d6a4c1..ac2a983 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -433,7 +433,7 @@ rfbHandleAuthResult(rfbClient* client) /* we have an error following */ if (!ReadFromRFBServer(client, (char *)&reasonLen, 4)) return FALSE; reasonLen = rfbClientSwap32IfLE(reasonLen); - reason = malloc(reasonLen+1); + reason = malloc((uint64_t)reasonLen+1); if (!ReadFromRFBServer(client, reason, reasonLen)) { free(reason); return FALSE; } reason[reasonLen]=0; rfbClientLog("VNC connection failed: %s\n",reason); @@ -461,7 +461,7 @@ ReadReason(rfbClient* client) /* we have an error following */ if (!ReadFromRFBServer(client, (char *)&reasonLen, 4)) return; reasonLen = rfbClientSwap32IfLE(reasonLen); - reason = malloc(reasonLen+1); + reason = malloc((uint64_t)reasonLen+1); if (!ReadFromRFBServer(client, reason, reasonLen)) { free(reason); return; } reason[reasonLen]=0; rfbClientLog("VNC connection failed: %s\n",reason); @@ -2187,10 +2187,12 @@ HandleRFBServerMessage(rfbClient* client) msg.sct.length = rfbClientSwap32IfLE(msg.sct.length); - buffer = malloc(msg.sct.length+1); + buffer = malloc((uint64_t)msg.sct.length+1); - if (!ReadFromRFBServer(client, buffer, msg.sct.length)) + if (!ReadFromRFBServer(client, buffer, msg.sct.length)) { + free(buffer); return FALSE; + } buffer[msg.sct.length] = 0; -- cgit v1.2.1 From 4a21bbd097ef7c44bb000c3bd0907f96a10e4ce7 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Mon, 1 Oct 2018 19:38:33 +0200 Subject: LibVNCClient: make sure Ultra decoding cannot dereference a null pointer Closes #254 --- libvncclient/ultra.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'libvncclient') diff --git a/libvncclient/ultra.c b/libvncclient/ultra.c index a82e2ed..a287526 100644 --- a/libvncclient/ultra.c +++ b/libvncclient/ultra.c @@ -66,6 +66,8 @@ HandleUltraBPP (rfbClient* client, int rx, int ry, int rw, int rh) if ((client->raw_buffer_size % 4)!=0) client->raw_buffer_size += (4-(client->raw_buffer_size % 4)); client->raw_buffer = (char*) malloc( client->raw_buffer_size ); + if(client->raw_buffer == NULL) + return FALSE; } /* allocate enough space to store the incoming compressed packet */ @@ -150,6 +152,8 @@ HandleUltraZipBPP (rfbClient* client, int rx, int ry, int rw, int rh) if ((client->raw_buffer_size % 4)!=0) client->raw_buffer_size += (4-(client->raw_buffer_size % 4)); client->raw_buffer = (char*) malloc( client->raw_buffer_size ); + if(client->raw_buffer == NULL) + return FALSE; } -- cgit v1.2.1 From 7b1ef0ffc4815cab9a96c7278394152bdc89dc4d Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Thu, 4 Oct 2018 22:27:39 +0200 Subject: LibVNCClient: really fix #250 --- libvncclient/corre.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libvncclient') diff --git a/libvncclient/corre.c b/libvncclient/corre.c index 55107b1..cbc986a 100644 --- a/libvncclient/corre.c +++ b/libvncclient/corre.c @@ -48,7 +48,7 @@ HandleCoRREBPP (rfbClient* client, int rx, int ry, int rw, int rh) client->GotFillRect(client, rx, ry, rw, rh, pix); - if (hdr.nSubrects * (4 + (BPP / 8)) > RFB_BUFFER_SIZE || !ReadFromRFBServer(client, client->buffer, hdr.nSubrects * (4 + (BPP / 8)))) + if (hdr.nSubrects > RFB_BUFFER_SIZE / (4 + (BPP / 8)) || !ReadFromRFBServer(client, client->buffer, hdr.nSubrects * (4 + (BPP / 8)))) return FALSE; ptr = (uint8_t *)client->buffer; -- cgit v1.2.1 From 597473be045c044771e07d2179bcfa5a365ef617 Mon Sep 17 00:00:00 2001 From: Tobias Junghans Date: Mon, 5 Nov 2018 15:52:59 +0100 Subject: LibVNCClient: free buffers in rfbClientCleanup() Buffers allocated by encoding handlers have to be freed as well. --- libvncclient/vncviewer.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'libvncclient') diff --git a/libvncclient/vncviewer.c b/libvncclient/vncviewer.c index 2a13f0e..ec1b73a 100644 --- a/libvncclient/vncviewer.c +++ b/libvncclient/vncviewer.c @@ -519,6 +519,12 @@ void rfbClientCleanup(rfbClient* client) { #endif #endif + if (client->ultra_buffer) + free(client->ultra_buffer); + + if (client->raw_buffer) + free(client->raw_buffer); + FreeTLS(client); while (client->clientData) { -- cgit v1.2.1 From 343eaabd7c910676a9985f1585e2cd4adebaaaa7 Mon Sep 17 00:00:00 2001 From: Tobias Junghans Date: Tue, 6 Nov 2018 10:23:19 +0100 Subject: LibVNCClient: init pad field for set encodings msg --- libvncclient/rfbproto.c | 1 + 1 file changed, 1 insertion(+) (limited to 'libvncclient') diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index ac2a983..26c4e7a 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -1253,6 +1253,7 @@ SetFormatAndEncodings(rfbClient* client) if (!SupportsClient2Server(client, rfbSetEncodings)) return TRUE; se->type = rfbSetEncodings; + se->pad = 0; se->nEncodings = 0; if (client->appData.encodingsString) { -- cgit v1.2.1 From 09b2ed438177a86bcf29e01bf0851ce815b6ef8d Mon Sep 17 00:00:00 2001 From: Tobias Junghans Date: Tue, 6 Nov 2018 10:32:14 +0100 Subject: Undef error codes before redefining them for WSA Fixes compiler warnings about redefined macros from errno.h. --- libvncclient/sasl.c | 3 +++ libvncclient/sockets.c | 3 +++ 2 files changed, 6 insertions(+) (limited to 'libvncclient') diff --git a/libvncclient/sasl.c b/libvncclient/sasl.c index 0530307..db240c1 100644 --- a/libvncclient/sasl.c +++ b/libvncclient/sasl.c @@ -41,6 +41,9 @@ #ifdef WIN32 #undef SOCKET #include +#ifdef EWOULDBLOCK +#undef EWOULDBLOCK +#endif #define EWOULDBLOCK WSAEWOULDBLOCK #define socklen_t int #define close closesocket diff --git a/libvncclient/sockets.c b/libvncclient/sockets.c index ed2deef..f042472 100644 --- a/libvncclient/sockets.c +++ b/libvncclient/sockets.c @@ -40,6 +40,9 @@ #ifdef WIN32 #undef SOCKET #include +#ifdef EWOULDBLOCK +#undef EWOULDBLOCK +#endif #define EWOULDBLOCK WSAEWOULDBLOCK #define close closesocket #define read(sock,buf,len) recv(sock,buf,len,0) -- cgit v1.2.1 From 9f5116eb538491c181bfd845efe9333ef5b5376d Mon Sep 17 00:00:00 2001 From: Tobias Junghans Date: Tue, 6 Nov 2018 10:36:36 +0100 Subject: LibVNCClient: pass buffer as const to WriteToTLS() --- libvncclient/tls.h | 2 +- libvncclient/tls_gnutls.c | 2 +- libvncclient/tls_none.c | 2 +- libvncclient/tls_openssl.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'libvncclient') diff --git a/libvncclient/tls.h b/libvncclient/tls.h index a5a2ac6..ffcfdeb 100644 --- a/libvncclient/tls.h +++ b/libvncclient/tls.h @@ -43,7 +43,7 @@ int ReadFromTLS(rfbClient* client, char *out, unsigned int n); * It's a wrapper function over gnutls_record_send() and it will be * blocking call, until all bytes are written or error returned. */ -int WriteToTLS(rfbClient* client, char *buf, unsigned int n); +int WriteToTLS(rfbClient* client, const char *buf, unsigned int n); /* Free TLS resources */ void FreeTLS(rfbClient* client); diff --git a/libvncclient/tls_gnutls.c b/libvncclient/tls_gnutls.c index f146d2a..ec3c450 100644 --- a/libvncclient/tls_gnutls.c +++ b/libvncclient/tls_gnutls.c @@ -589,7 +589,7 @@ ReadFromTLS(rfbClient* client, char *out, unsigned int n) } int -WriteToTLS(rfbClient* client, char *buf, unsigned int n) +WriteToTLS(rfbClient* client, const char *buf, unsigned int n) { unsigned int offset = 0; ssize_t ret; diff --git a/libvncclient/tls_none.c b/libvncclient/tls_none.c index 4dfcb27..d436ce9 100644 --- a/libvncclient/tls_none.c +++ b/libvncclient/tls_none.c @@ -43,7 +43,7 @@ int ReadFromTLS(rfbClient* client, char *out, unsigned int n) } -int WriteToTLS(rfbClient* client, char *buf, unsigned int n) +int WriteToTLS(rfbClient* client, const char *buf, unsigned int n) { rfbClientLog("TLS is not supported.\n"); errno = EINTR; diff --git a/libvncclient/tls_openssl.c b/libvncclient/tls_openssl.c index fe60147..e2fadb2 100644 --- a/libvncclient/tls_openssl.c +++ b/libvncclient/tls_openssl.c @@ -640,7 +640,7 @@ ReadFromTLS(rfbClient* client, char *out, unsigned int n) } int -WriteToTLS(rfbClient* client, char *buf, unsigned int n) +WriteToTLS(rfbClient* client, const char *buf, unsigned int n) { unsigned int offset = 0; ssize_t ret; -- cgit v1.2.1 From a70d3b2710b8fec3d7f15ff12da1becaccfe12e8 Mon Sep 17 00:00:00 2001 From: Tobias Junghans Date: Wed, 7 Nov 2018 11:11:19 +0100 Subject: common: d3des: drop unused rfbCPKey() --- libvncclient/rfbproto.c | 1 - 1 file changed, 1 deletion(-) (limited to 'libvncclient') diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index ac2a983..a024bb4 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -2426,7 +2426,6 @@ PrintPixelFormat(rfbPixelFormat *format) #define rfbDes rfbClientDes #define rfbDesKey rfbClientDesKey #define rfbUseKey rfbClientUseKey -#define rfbCPKey rfbClientCPKey #include "vncauth.c" #include "d3des.c" -- cgit v1.2.1 From 5f9a07d7e1613dbccd5a27e845dbb8a7f2a27b2e Mon Sep 17 00:00:00 2001 From: Tobias Junghans Date: Tue, 6 Nov 2018 14:22:56 +0100 Subject: LibVNCClient: add support for custom auth handlers This allows to register custom authentication handlers in order to support additional security types. --- libvncclient/rfbproto.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'libvncclient') diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index ac2a983..fbe579c 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -474,9 +474,11 @@ ReadSupportedSecurityType(rfbClient* client, uint32_t *result, rfbBool subAuth) uint8_t count=0; uint8_t loop=0; uint8_t flag=0; + rfbBool extAuthHandler; uint8_t tAuth[256]; char buf1[500],buf2[10]; uint32_t authScheme; + rfbClientProtocolExtension* e; if (!ReadFromRFBServer(client, (char *)&count, 1)) return FALSE; @@ -495,7 +497,18 @@ ReadSupportedSecurityType(rfbClient* client, uint32_t *result, rfbBool subAuth) if (!ReadFromRFBServer(client, (char *)&tAuth[loop], 1)) return FALSE; rfbClientLog("%d) Received security type %d\n", loop, tAuth[loop]); if (flag) continue; + extAuthHandler=FALSE; + for (e = rfbClientExtensions; e; e = e->next) { + if (!e->handleAuthentication) continue; + uint32_t const* secType; + for (secType = e->securityTypes; secType && *secType; secType++) { + if (tAuth[loop]==*secType) { + extAuthHandler=TRUE; + } + } + } if (tAuth[loop]==rfbVncAuth || tAuth[loop]==rfbNoAuth || + extAuthHandler || #if defined(LIBVNCSERVER_HAVE_GNUTLS) || defined(LIBVNCSERVER_HAVE_LIBSSL) tAuth[loop]==rfbVeNCrypt || #endif @@ -1176,6 +1189,22 @@ InitialiseRFBConnection(rfbClient* client) break; default: + { + rfbBool authHandled=FALSE; + rfbClientProtocolExtension* e; + for (e = rfbClientExtensions; e; e = e->next) { + uint32_t const* secType; + if (!e->handleAuthentication) continue; + for (secType = e->securityTypes; secType && *secType; secType++) { + if (authScheme==*secType) { + if (!e->handleAuthentication(client, authScheme)) return FALSE; + if (!rfbHandleAuthResult(client)) return FALSE; + authHandled=TRUE; + } + } + } + if (authHandled) break; + } rfbClientLog("Unknown authentication scheme from VNC server: %d\n", (int)authScheme); return FALSE; -- cgit v1.2.1 From e66a8a17f3fb2dc87ebd35535c9a310068ba3b4a Mon Sep 17 00:00:00 2001 From: Tobias Junghans Date: Thu, 22 Nov 2018 15:19:37 +0100 Subject: Allow to use global LZO library instead of miniLZO The complete LZO library nowadays is installed on many systems so we can optionally make use of it and omit internal miniLZO implementation. --- libvncclient/rfbproto.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'libvncclient') diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index 4541e0d..82536cd 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -61,7 +61,11 @@ #endif #include "sasl.h" +#ifdef LIBVNCSERVER_HAVE_LZO +#include +#else #include "minilzo.h" +#endif #include "tls.h" #ifdef _MSC_VER -- cgit v1.2.1 From c5ba3fee85a7ecbbca1df5ffd46d32b92757bc2a Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sat, 29 Dec 2018 14:16:58 +0100 Subject: LibVNCClient: ignore server-sent cut text longer than 1MB This is in line with how LibVNCServer does it (28afb6c537dc82ba04d5f245b15ca7205c6dbb9c) and fixes part of #273. --- libvncclient/rfbproto.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'libvncclient') diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index 4541e0d..8792dbf 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -2217,6 +2217,11 @@ HandleRFBServerMessage(rfbClient* client) msg.sct.length = rfbClientSwap32IfLE(msg.sct.length); + if (msg.sct.length > 1<<20) { + rfbClientErr("Ignoring too big cut text length sent by server: %u B > 1 MB\n", (unsigned int)msg.sct.length); + return FALSE; + } + buffer = malloc((uint64_t)msg.sct.length+1); if (!ReadFromRFBServer(client, buffer, msg.sct.length)) { -- cgit v1.2.1 From e34bcbb759ca5bef85809967a268fdf214c1ad2c Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sat, 29 Dec 2018 14:40:53 +0100 Subject: LibVNCClient: ignore server-sent reason strings longer than 1MB Fixes #273 --- libvncclient/rfbproto.c | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) (limited to 'libvncclient') diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index 8792dbf..ba7d70a 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -412,11 +412,29 @@ rfbBool ConnectToRFBRepeater(rfbClient* client,const char *repeaterHost, int rep extern void rfbClientEncryptBytes(unsigned char* bytes, char* passwd); extern void rfbClientEncryptBytes2(unsigned char *where, const int length, unsigned char *key); +static void +ReadReason(rfbClient* client) +{ + uint32_t reasonLen; + char *reason; + + if (!ReadFromRFBServer(client, (char *)&reasonLen, 4)) return; + reasonLen = rfbClientSwap32IfLE(reasonLen); + if(reasonLen > 1<<20) { + rfbClientLog("VNC connection failed, but sent reason length of %u exceeds limit of 1MB",(unsigned int)reasonLen); + return; + } + reason = malloc(reasonLen+1); + if (!ReadFromRFBServer(client, reason, reasonLen)) { free(reason); return; } + reason[reasonLen]=0; + rfbClientLog("VNC connection failed: %s\n",reason); + free(reason); +} + rfbBool rfbHandleAuthResult(rfbClient* client) { - uint32_t authResult=0, reasonLen=0; - char *reason=NULL; + uint32_t authResult=0; if (!ReadFromRFBServer(client, (char *)&authResult, 4)) return FALSE; @@ -431,13 +449,7 @@ rfbHandleAuthResult(rfbClient* client) if (client->major==3 && client->minor>7) { /* we have an error following */ - if (!ReadFromRFBServer(client, (char *)&reasonLen, 4)) return FALSE; - reasonLen = rfbClientSwap32IfLE(reasonLen); - reason = malloc((uint64_t)reasonLen+1); - if (!ReadFromRFBServer(client, reason, reasonLen)) { free(reason); return FALSE; } - reason[reasonLen]=0; - rfbClientLog("VNC connection failed: %s\n",reason); - free(reason); + ReadReason(client); return FALSE; } rfbClientLog("VNC authentication failed\n"); @@ -452,21 +464,6 @@ rfbHandleAuthResult(rfbClient* client) return FALSE; } -static void -ReadReason(rfbClient* client) -{ - uint32_t reasonLen; - char *reason; - - /* we have an error following */ - if (!ReadFromRFBServer(client, (char *)&reasonLen, 4)) return; - reasonLen = rfbClientSwap32IfLE(reasonLen); - reason = malloc((uint64_t)reasonLen+1); - if (!ReadFromRFBServer(client, reason, reasonLen)) { free(reason); return; } - reason[reasonLen]=0; - rfbClientLog("VNC connection failed: %s\n",reason); - free(reason); -} static rfbBool ReadSupportedSecurityType(rfbClient* client, uint32_t *result, rfbBool subAuth) -- cgit v1.2.1 From c2c4b81e6cb3b485fb1ec7ba9e7defeb889f6ba7 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 6 Jan 2019 14:20:37 +0100 Subject: LibVNCClient: fail on server-sent desktop name lengths longer than 1MB re #273 --- libvncclient/rfbproto.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'libvncclient') diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index e56e778..6af21a5 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -1224,8 +1224,12 @@ InitialiseRFBConnection(rfbClient* client) client->si.format.blueMax = rfbClientSwap16IfLE(client->si.format.blueMax); client->si.nameLength = rfbClientSwap32IfLE(client->si.nameLength); - /* To guard against integer wrap-around, si.nameLength is cast to 64 bit */ - client->desktopName = malloc((uint64_t)client->si.nameLength + 1); + if (client->si.nameLength > 1<<20) { + rfbClientErr("Too big desktop name length sent by server: %u B > 1 MB\n", (unsigned int)client->si.nameLength); + return FALSE; + } + + client->desktopName = malloc(client->si.nameLength + 1); if (!client->desktopName) { rfbClientLog("Error allocating memory for desktop name, %lu bytes\n", (unsigned long)client->si.nameLength); -- cgit v1.2.1 From a64c3b37af9a6c8f8009d7516874b8d266b42bae Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 6 Jan 2019 14:22:34 +0100 Subject: LibVNCClient: remove now-useless cast re #273 --- libvncclient/rfbproto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libvncclient') diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index 6af21a5..2f887c3 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -2227,7 +2227,7 @@ HandleRFBServerMessage(rfbClient* client) return FALSE; } - buffer = malloc((uint64_t)msg.sct.length+1); + buffer = malloc(msg.sct.length+1); if (!ReadFromRFBServer(client, buffer, msg.sct.length)) { free(buffer); -- cgit v1.2.1