You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libtdevnc/libvncclient/rfbproto.c

2448 lines
70 KiB

/*
* Copyright (C) 2000-2002 Constantin Kaplinsky. All Rights Reserved.
* Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
* Copyright (C) 1999 AT&T Laboratories Cambridge. 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.
*/
/*
* rfbproto.c - functions to deal with client side of RFB protocol.
*/
#ifdef __STRICT_ANSI__
#define _BSD_SOURCE
#define _POSIX_SOURCE
#define _XOPEN_SOURCE 600
#endif
#ifndef WIN32
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
19 years ago
#endif
#include <errno.h>
#include <rfb/rfbclient.h>
#ifdef WIN32
#undef SOCKET
#undef socklen_t
#endif
#ifdef LIBVNCSERVER_HAVE_LIBZ
#include <zlib.h>
#ifdef __CHECKER__
#undef Z_NULL
#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 <jpeglib.h>
#endif
#ifndef _MSC_VER
/* Strings.h is not available in MSVC */
#include <strings.h>
#endif
#include <stdarg.h>
#include <time.h>
#ifdef LIBVNCSERVER_WITH_CLIENT_GCRYPT
#include <gcrypt.h>
#endif
#include "minilzo.h"
#include "tls.h"
#ifdef _MSC_VER
# define snprintf _snprintf /* MSVC went straight to the underscored syntax */
#endif
/*
* rfbClientLog prints a time-stamped message to the log file (stderr).
*/
rfbBool rfbEnableClientLogging=TRUE;
static void
rfbDefaultClientLog(const char *format, ...)
{
va_list args;
char buf[256];
time_t log_clock;
if(!rfbEnableClientLogging)
return;
va_start(args, format);
time(&log_clock);
strftime(buf, 255, "%d/%m/%Y %X ", localtime(&log_clock));
fprintf(stderr, "%s", buf);
vfprintf(stderr, format, args);
fflush(stderr);
va_end(args);
}
rfbClientLogProc rfbClientLog=rfbDefaultClientLog;
rfbClientLogProc rfbClientErr=rfbDefaultClientLog;
/* extensions */
rfbClientProtocolExtension* rfbClientExtensions = NULL;
void rfbClientRegisterExtension(rfbClientProtocolExtension* e)
{
e->next = rfbClientExtensions;
rfbClientExtensions = e;
}
/* client data */
void rfbClientSetClientData(rfbClient* client, void* tag, void* data)
{
rfbClientData* clientData = client->clientData;
while(clientData && clientData->tag != tag)
clientData = clientData->next;
if(clientData == NULL) {
clientData = calloc(sizeof(rfbClientData), 1);
clientData->next = client->clientData;
client->clientData = clientData;
clientData->tag = tag;
}
clientData->data = data;
}
void* rfbClientGetClientData(rfbClient* client, void* tag)
{
rfbClientData* clientData = client->clientData;
while(clientData) {
if(clientData->tag == tag)
return clientData->data;
clientData = clientData->next;
}
return NULL;
}
/* messages */
static void FillRectangle(rfbClient* client, int x, int y, int w, int h, uint32_t colour) {
int i,j;
#define FILL_RECT(BPP) \
for(j=y*client->width;j<(y+h)*client->width;j+=client->width) \
for(i=x;i<x+w;i++) \
((uint##BPP##_t*)client->frameBuffer)[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;
LibVNCClient: Add H.264 encoding for framebuffer updates This patch implements support in LibVNCClient for framebuffer updates encoded as H.264 frames. Hardware accelerated decoding is performed using VA API. This is experimental support to let the community explore the possibilities offered by the potential bandwidth and latency reductions that H.264 encoding allows. This may be particularly useful for use cases such as online gaming, hosted desktops, hosted set top boxes... This patch only provides the client side support and is meant to be used with corresponding server-side support, as provided by an upcoming patch for qemu ui/vnc module (to view the display of a virtual machine executing under QEMU). With this H.264-based encoding, if multiple framebuffer update messages are generated for a single server framebuffer modification, the H.264 frame data is sent only with the first update message. Subsequent update framebuffer messages will contain only the coordinates and size of the additional updated regions. Instructions/Requirements: * The patch should be applied on top of the previous patch I submitted with minor enhancements to the gtkvncviewer application: http://sourceforge.net/mailarchive/message.php?msg_id=30323804 * Currently only works with libva 1.0: use branch "v1.0-branch" for libva and intel-driver. Those can be built as follows: cd libva git checkout v1.0-branch ./autogen.sh make sudo make install cd .. git clone git://anongit.freedesktop.org/vaapi/intel-driver cd intel-driver git checkout v1.0-branch ./autogen.sh make sudo make install Signed-off-by: David Verbeiren <david.verbeiren@intel.com>
11 years ago
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;
#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);
static rfbBool HandleCoRRE8(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleCoRRE16(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleCoRRE32(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleHextile8(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleHextile16(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleHextile32(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleUltra8(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleUltra16(rfbClient* client, int rx, int ry, int rw, int rh);
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);
#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);
static rfbBool HandleZlib32(rfbClient* client, int rx, int ry, int rw, int rh);
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
static rfbBool HandleTight8(rfbClient* client, int rx, int ry, int rw, int rh);
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);
static rfbBool HandleZRLE16(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleZRLE24(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleZRLE24Up(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleZRLE24Down(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleZRLE32(rfbClient* client, int rx, int ry, int rw, int rh);
#endif
LibVNCClient: Add H.264 encoding for framebuffer updates This patch implements support in LibVNCClient for framebuffer updates encoded as H.264 frames. Hardware accelerated decoding is performed using VA API. This is experimental support to let the community explore the possibilities offered by the potential bandwidth and latency reductions that H.264 encoding allows. This may be particularly useful for use cases such as online gaming, hosted desktops, hosted set top boxes... This patch only provides the client side support and is meant to be used with corresponding server-side support, as provided by an upcoming patch for qemu ui/vnc module (to view the display of a virtual machine executing under QEMU). With this H.264-based encoding, if multiple framebuffer update messages are generated for a single server framebuffer modification, the H.264 frame data is sent only with the first update message. Subsequent update framebuffer messages will contain only the coordinates and size of the additional updated regions. Instructions/Requirements: * The patch should be applied on top of the previous patch I submitted with minor enhancements to the gtkvncviewer application: http://sourceforge.net/mailarchive/message.php?msg_id=30323804 * Currently only works with libva 1.0: use branch "v1.0-branch" for libva and intel-driver. Those can be built as follows: cd libva git checkout v1.0-branch ./autogen.sh make sudo make install cd .. git clone git://anongit.freedesktop.org/vaapi/intel-driver cd intel-driver git checkout v1.0-branch ./autogen.sh make sudo make install Signed-off-by: David Verbeiren <david.verbeiren@intel.com>
11 years ago
#ifdef LIBVNCSERVER_CONFIG_LIBVA
static rfbBool HandleH264 (rfbClient* client, int rx, int ry, int rw, int rh);
#endif
/*
* Server Capability Functions
*/
rfbBool
SupportsClient2Server(rfbClient* client, int messageType)
{
return (client->supportedMessages.client2server[((messageType & 0xFF)/8)] & (1<<(messageType % 8)) ? TRUE : FALSE);
}
rfbBool
SupportsServer2Client(rfbClient* client, int messageType)
{
return (client->supportedMessages.server2client[((messageType & 0xFF)/8)] & (1<<(messageType % 8)) ? TRUE : FALSE);
}
void
SetClient2Server(rfbClient* client, int messageType)
{
client->supportedMessages.client2server[((messageType & 0xFF)/8)] |= (1<<(messageType % 8));
}
void
SetServer2Client(rfbClient* client, int messageType)
{
client->supportedMessages.server2client[((messageType & 0xFF)/8)] |= (1<<(messageType % 8));
}
void
ClearClient2Server(rfbClient* client, int messageType)
{
client->supportedMessages.client2server[((messageType & 0xFF)/8)] &= (!(1<<(messageType % 8)));
}
void
ClearServer2Client(rfbClient* client, int messageType)
{
client->supportedMessages.server2client[((messageType & 0xFF)/8)] &= (!(1<<(messageType % 8)));
}
void
DefaultSupportedMessages(rfbClient* client)
{
memset((char *)&client->supportedMessages,0,sizeof(client->supportedMessages));
/* Default client supported messages (universal RFB 3.3 protocol) */
SetClient2Server(client, rfbSetPixelFormat);
/* SetClient2Server(client, rfbFixColourMapEntries); Not currently supported */
SetClient2Server(client, rfbSetEncodings);
SetClient2Server(client, rfbFramebufferUpdateRequest);
SetClient2Server(client, rfbKeyEvent);
SetClient2Server(client, rfbPointerEvent);
SetClient2Server(client, rfbClientCutText);
/* technically, we only care what we can *send* to the server
* but, we set Server2Client Just in case it ever becomes useful
*/
SetServer2Client(client, rfbFramebufferUpdate);
SetServer2Client(client, rfbSetColourMapEntries);
SetServer2Client(client, rfbBell);
SetServer2Client(client, rfbServerCutText);
}
void
DefaultSupportedMessagesUltraVNC(rfbClient* client)
{
DefaultSupportedMessages(client);
SetClient2Server(client, rfbFileTransfer);
SetClient2Server(client, rfbSetScale);
SetClient2Server(client, rfbSetServerInput);
SetClient2Server(client, rfbSetSW);
SetClient2Server(client, rfbTextChat);
SetClient2Server(client, rfbPalmVNCSetScaleFactor);
/* technically, we only care what we can *send* to the server */
SetServer2Client(client, rfbResizeFrameBuffer);
SetServer2Client(client, rfbPalmVNCReSizeFrameBuffer);
SetServer2Client(client, rfbFileTransfer);
SetServer2Client(client, rfbTextChat);
}
void
DefaultSupportedMessagesTightVNC(rfbClient* client)
{
DefaultSupportedMessages(client);
SetClient2Server(client, rfbFileTransfer);
SetClient2Server(client, rfbSetServerInput);
SetClient2Server(client, rfbSetSW);
/* SetClient2Server(client, rfbTextChat); */
/* technically, we only care what we can *send* to the server */
SetServer2Client(client, rfbFileTransfer);
SetServer2Client(client, rfbTextChat);
}
#ifndef WIN32
static rfbBool
IsUnixSocket(const char *name)
{
struct stat sb;
if(stat(name, &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFSOCK)
return TRUE;
return FALSE;
}
#endif
/*
* ConnectToRFBServer.
*/
rfbBool
ConnectToRFBServer(rfbClient* client,const char *hostname, int port)
{
if (client->serverPort==-1) {
/* serverHost is a file recorded by vncrec. */
const char* magic="vncLog0.0";
char buffer[10];
rfbVNCRec* rec = (rfbVNCRec*)malloc(sizeof(rfbVNCRec));
client->vncRec = rec;
rec->file = fopen(client->serverHost,"rb");
rec->tv.tv_sec = 0;
rec->readTimestamp = FALSE;
rec->doNotSleep = FALSE;
if (!rec->file) {
rfbClientLog("Could not open %s.\n",client->serverHost);
return FALSE;
}
setbuf(rec->file,NULL);
if (fread(buffer,1,strlen(magic),rec->file) != strlen(magic) || strncmp(buffer,magic,strlen(magic))) {
rfbClientLog("File %s was not recorded by vncrec.\n",client->serverHost);
fclose(rec->file);
return FALSE;
}
client->sock = -1;
return TRUE;
}
#ifndef WIN32
if(IsUnixSocket(hostname))
/* serverHost is a UNIX socket. */
client->sock = ConnectClientToUnixSock(hostname);
else
#endif
{
#ifdef LIBVNCSERVER_IPv6
client->sock = ConnectClientToTcpAddr6(hostname, port);
if (client->sock == -1)
#endif
{
unsigned int host;
/* serverHost is a hostname */
if (!StringToIPAddr(hostname, &host)) {
rfbClientLog("Couldn't convert '%s' to host address\n", hostname);
return FALSE;
}
client->sock = ConnectClientToTcpAddr(host, port);
}
}
if (client->sock < 0) {
rfbClientLog("Unable to connect to VNC server\n");
return FALSE;
}
if(client->QoS_DSCP && !SetDSCP(client->sock, client->QoS_DSCP))
return FALSE;
return SetNonBlocking(client->sock);
}
/*
* ConnectToRFBRepeater.
*/
rfbBool ConnectToRFBRepeater(rfbClient* client,const char *repeaterHost, int repeaterPort, const char *destHost, int destPort)
{
rfbProtocolVersionMsg pv;
int major,minor;
char tmphost[250];
#ifdef LIBVNCSERVER_IPv6
client->sock = ConnectClientToTcpAddr6(repeaterHost, repeaterPort);
if (client->sock == -1)
#endif
{
unsigned int host;
if (!StringToIPAddr(repeaterHost, &host)) {
rfbClientLog("Couldn't convert '%s' to host address\n", repeaterHost);
return FALSE;
}
client->sock = ConnectClientToTcpAddr(host, repeaterPort);
}
if (client->sock < 0) {
rfbClientLog("Unable to connect to VNC repeater\n");
return FALSE;
}
if (!SetNonBlocking(client->sock))
return FALSE;
if (!ReadFromRFBServer(client, pv, sz_rfbProtocolVersionMsg))
return FALSE;
pv[sz_rfbProtocolVersionMsg] = 0;
/* UltraVNC repeater always report version 000.000 to identify itself */
if (sscanf(pv,rfbProtocolVersionFormat,&major,&minor) != 2 || major != 0 || minor != 0) {
rfbClientLog("Not a valid VNC repeater (%s)\n",pv);
return FALSE;
}
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)))
return FALSE;
return TRUE;
}
extern void rfbClientEncryptBytes(unsigned char* bytes, char* passwd);
extern void rfbClientEncryptBytes2(unsigned char *where, const int length, unsigned char *key);
rfbBool
rfbHandleAuthResult(rfbClient* client)
{
uint32_t authResult=0, reasonLen=0;
char *reason=NULL;
if (!ReadFromRFBServer(client, (char *)&authResult, 4)) return FALSE;
authResult = rfbClientSwap32IfLE(authResult);
switch (authResult) {
case rfbVncAuthOK:
rfbClientLog("VNC authentication succeeded\n");
return TRUE;
break;
case rfbVncAuthFailed:
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(reasonLen+1);
if (!ReadFromRFBServer(client, reason, reasonLen)) { free(reason); return FALSE; }
reason[reasonLen]=0;
rfbClientLog("VNC connection failed: %s\n",reason);
free(reason);
return FALSE;
}
rfbClientLog("VNC authentication failed\n");
return FALSE;
case rfbVncAuthTooMany:
rfbClientLog("VNC authentication failed - too many tries\n");
return FALSE;
}
rfbClientLog("Unknown VNC authentication result: %d\n",
(int)authResult);
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(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)
{
uint8_t count=0;
uint8_t loop=0;
uint8_t flag=0;
uint8_t tAuth[256];
char buf1[500],buf2[10];
uint32_t authScheme;
if (!ReadFromRFBServer(client, (char *)&count, 1)) return FALSE;
if (count==0)
{
rfbClientLog("List of security types is ZERO, expecting an error to follow\n");
ReadReason(client);
return FALSE;
}
rfbClientLog("We have %d security types to read\n", count);
authScheme=0;
/* now, we have a list of available security types to read ( uint8_t[] ) */
for (loop=0;loop<count;loop++)
{
if (!ReadFromRFBServer(client, (char *)&tAuth[loop], 1)) return FALSE;
rfbClientLog("%d) Received security type %d\n", loop, tAuth[loop]);
if (flag) continue;
if (tAuth[loop]==rfbVncAuth || tAuth[loop]==rfbNoAuth ||
#if defined(LIBVNCSERVER_HAVE_GNUTLS) || defined(LIBVNCSERVER_HAVE_LIBSSL)
tAuth[loop]==rfbVeNCrypt ||
#endif
(tAuth[loop]==rfbARD && client->GetCredential) ||
(!subAuth && (tAuth[loop]==rfbTLS || (tAuth[loop]==rfbVeNCrypt && client->GetCredential))))
{
if (!subAuth && client->clientAuthSchemes)
{
int i;
for (i=0;client->clientAuthSchemes[i];i++)
{
if (client->clientAuthSchemes[i]==(uint32_t)tAuth[loop])
{
flag++;
authScheme=tAuth[loop];
break;
}
}
}
else
{
flag++;
authScheme=tAuth[loop];
}
if (flag)
{
rfbClientLog("Selecting security type %d (%d/%d in the list)\n", authScheme, loop, count);
/* send back a single byte indicating which security type to use */
if (!WriteToRFBServer(client, (char *)&tAuth[loop], 1)) return FALSE;
}
}
}
if (authScheme==0)
{
memset(buf1, 0, sizeof(buf1));
for (loop=0;loop<count;loop++)
{
if (strlen(buf1)>=sizeof(buf1)-1) break;
snprintf(buf2, sizeof(buf2), (loop>0 ? ", %d" : "%d"), (int)tAuth[loop]);
strncat(buf1, buf2, sizeof(buf1)-strlen(buf1)-1);
}
rfbClientLog("Unknown authentication scheme from VNC server: %s\n",
buf1);
return FALSE;
}
*result = authScheme;
return TRUE;
}
static rfbBool
HandleVncAuth(rfbClient *client)
{
uint8_t challenge[CHALLENGESIZE];
char *passwd=NULL;
int i;
if (!ReadFromRFBServer(client, (char *)challenge, CHALLENGESIZE)) return FALSE;
if (client->serverPort!=-1) { /* if not playing a vncrec file */
if (client->GetPassword)
passwd = client->GetPassword(client);
if ((!passwd) || (strlen(passwd) == 0)) {
rfbClientLog("Reading password failed\n");
return FALSE;
}
if (strlen(passwd) > 8) {
passwd[8] = '\0';
}
rfbClientEncryptBytes(challenge, passwd);
/* Lose the password from memory */
for (i = strlen(passwd); i >= 0; i--) {
passwd[i] = '\0';
}
free(passwd);
if (!WriteToRFBServer(client, (char *)challenge, CHALLENGESIZE)) return FALSE;
}
/* Handle the SecurityResult message */
if (!rfbHandleAuthResult(client)) return FALSE;
return TRUE;
}
static void
FreeUserCredential(rfbCredential *cred)
{
if (cred->userCredential.username) free(cred->userCredential.username);
if (cred->userCredential.password) free(cred->userCredential.password);
free(cred);
}
static rfbBool
HandlePlainAuth(rfbClient *client)
{
uint32_t ulen, ulensw;
uint32_t plen, plensw;
rfbCredential *cred;
if (!client->GetCredential)
{
rfbClientLog("GetCredential callback is not set.\n");
return FALSE;
}
cred = client->GetCredential(client, rfbCredentialTypeUser);
if (!cred)
{
rfbClientLog("Reading credential failed\n");
return FALSE;
}
ulen = (cred->userCredential.username ? strlen(cred->userCredential.username) : 0);
ulensw = rfbClientSwap32IfLE(ulen);
plen = (cred->userCredential.password ? strlen(cred->userCredential.password) : 0);
plensw = rfbClientSwap32IfLE(plen);
if (!WriteToRFBServer(client, (char *)&ulensw, 4) ||
!WriteToRFBServer(client, (char *)&plensw, 4))
{
FreeUserCredential(cred);
return FALSE;
}
if (ulen > 0)
{
if (!WriteToRFBServer(client, cred->userCredential.username, ulen))
{
FreeUserCredential(cred);
return FALSE;
}
}
if (plen > 0)
{
if (!WriteToRFBServer(client, cred->userCredential.password, plen))
{
FreeUserCredential(cred);
return FALSE;
}
}
FreeUserCredential(cred);
/* Handle the SecurityResult message */
if (!rfbHandleAuthResult(client)) return FALSE;
return TRUE;
}
/* Simple 64bit big integer arithmetic implementation */
/* (x + y) % m, works even if (x + y) > 64bit */
#define rfbAddM64(x,y,m) ((x+y)%m+(x+y<x?(((uint64_t)-1)%m+1)%m:0))
/* (x * y) % m */
static uint64_t
rfbMulM64(uint64_t x, uint64_t y, uint64_t m)
{
uint64_t r;
for(r=0;x>0;x>>=1)
{
if (x&1) r=rfbAddM64(r,y,m);
y=rfbAddM64(y,y,m);
}
return r;
}
/* (x ^ y) % m */
static uint64_t
rfbPowM64(uint64_t b, uint64_t e, uint64_t m)
{
uint64_t r;
for(r=1;e>0;e>>=1)
{
if(e&1) r=rfbMulM64(r,b,m);
b=rfbMulM64(b,b,m);
}
return r;
}
static rfbBool
HandleMSLogonAuth(rfbClient *client)
{
uint64_t gen, mod, resp, priv, pub, key;
uint8_t username[256], password[64];
rfbCredential *cred;
if (!ReadFromRFBServer(client, (char *)&gen, 8)) return FALSE;
if (!ReadFromRFBServer(client, (char *)&mod, 8)) return FALSE;
if (!ReadFromRFBServer(client, (char *)&resp, 8)) return FALSE;
gen = rfbClientSwap64IfLE(gen);
mod = rfbClientSwap64IfLE(mod);
resp = rfbClientSwap64IfLE(resp);
if (!client->GetCredential)
{
rfbClientLog("GetCredential callback is not set.\n");
return FALSE;
}
rfbClientLog("WARNING! MSLogon security type has very low password encryption! "\
"Use it only with SSH tunnel or trusted network.\n");
cred = client->GetCredential(client, rfbCredentialTypeUser);
if (!cred)
{
rfbClientLog("Reading credential failed\n");
return FALSE;
}
memset(username, 0, sizeof(username));
strncpy((char *)username, cred->userCredential.username, sizeof(username));
memset(password, 0, sizeof(password));
strncpy((char *)password, cred->userCredential.password, sizeof(password));
FreeUserCredential(cred);
srand(time(NULL));
priv = ((uint64_t)rand())<<32;
priv |= (uint64_t)rand();
pub = rfbPowM64(gen, priv, mod);
key = rfbPowM64(resp, priv, mod);
pub = rfbClientSwap64IfLE(pub);
key = rfbClientSwap64IfLE(key);
rfbClientEncryptBytes2(username, sizeof(username), (unsigned char *)&key);
rfbClientEncryptBytes2(password, sizeof(password), (unsigned char *)&key);
if (!WriteToRFBServer(client, (char *)&pub, 8)) return FALSE;
if (!WriteToRFBServer(client, (char *)username, sizeof(username))) return FALSE;
if (!WriteToRFBServer(client, (char *)password, sizeof(password))) return FALSE;
/* Handle the SecurityResult message */
if (!rfbHandleAuthResult(client)) return FALSE;
return TRUE;
}
#ifdef LIBVNCSERVER_WITH_CLIENT_GCRYPT
static rfbBool
rfbMpiToBytes(const gcry_mpi_t value, uint8_t *result, size_t size)
{
gcry_error_t error;
size_t len;
int i;
error = gcry_mpi_print(GCRYMPI_FMT_USG, result, size, &len, value);
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
{
rfbClientLog("gcry_mpi_print error: %s\n", gcry_strerror(error));
return FALSE;
}
for (i=size-1;i>(int)size-1-(int)len;--i)
result[i] = result[i-size+len];
for (;i>=0;--i)
result[i] = 0;
return TRUE;
}
static rfbBool
HandleARDAuth(rfbClient *client)
{
uint8_t gen[2], len[2];
size_t keylen;
uint8_t *mod = NULL, *resp, *pub, *key, *shared;
gcry_mpi_t genmpi = NULL, modmpi = NULL, respmpi = NULL;
gcry_mpi_t privmpi = NULL, pubmpi = NULL, keympi = NULL;
gcry_md_hd_t md5 = NULL;
gcry_cipher_hd_t aes = NULL;
gcry_error_t error;
uint8_t userpass[128], ciphertext[128];
int passwordLen, usernameLen;
rfbCredential *cred = NULL;
rfbBool result = FALSE;
while (1)
{
if (!ReadFromRFBServer(client, (char *)gen, 2))
break;
if (!ReadFromRFBServer(client, (char *)len, 2))
break;
if (!client->GetCredential)
{
rfbClientLog("GetCredential callback is not set.\n");
break;
}
cred = client->GetCredential(client, rfbCredentialTypeUser);
if (!cred)
{
rfbClientLog("Reading credential failed\n");
break;
}
keylen = 256*len[0]+len[1];
mod = (uint8_t*)malloc(keylen*4);
if (!mod)
{
rfbClientLog("malloc out of memory\n");
break;
}
resp = mod+keylen;
pub = resp+keylen;
key = pub+keylen;
if (!ReadFromRFBServer(client, (char *)mod, keylen))
break;
if (!ReadFromRFBServer(client, (char *)resp, keylen))
break;
error = gcry_mpi_scan(&genmpi, GCRYMPI_FMT_USG, gen, 2, NULL);
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
{
rfbClientLog("gcry_mpi_scan error: %s\n", gcry_strerror(error));
break;
}
error = gcry_mpi_scan(&modmpi, GCRYMPI_FMT_USG, mod, keylen, NULL);
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
{
rfbClientLog("gcry_mpi_scan error: %s\n", gcry_strerror(error));
break;
}
error = gcry_mpi_scan(&respmpi, GCRYMPI_FMT_USG, resp, keylen, NULL);
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
{
rfbClientLog("gcry_mpi_scan error: %s\n", gcry_strerror(error));
break;
}
privmpi = gcry_mpi_new(keylen);
if (!privmpi)
{
rfbClientLog("gcry_mpi_new out of memory\n");
break;
}
gcry_mpi_randomize(privmpi, (keylen/8)*8, GCRY_STRONG_RANDOM);
pubmpi = gcry_mpi_new(keylen);
if (!pubmpi)
{
rfbClientLog("gcry_mpi_new out of memory\n");
break;
}
gcry_mpi_powm(pubmpi, genmpi, privmpi, modmpi);
keympi = gcry_mpi_new(keylen);
if (!keympi)
{
rfbClientLog("gcry_mpi_new out of memory\n");
break;
}
gcry_mpi_powm(keympi, respmpi, privmpi, modmpi);