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

2452 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;
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;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;
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);
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
/*
* 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;
if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
{
/* Application did not initialize gcrypt, so we should */
if (!gcry_check_version(GCRYPT_VERSION))
{
/* Older version of libgcrypt is installed on system than compiled against */
rfbClientLog("libgcrypt version mismatch.\n");
}
}
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);