Commit f49a2927 authored by Johannes Schindelin's avatar Johannes Schindelin

Merge branch 'VeNCrypt'

parents 67223b7c 29990f00
......@@ -681,6 +681,22 @@ if test ! -z "$MINGW"; then
fi
AC_SUBST(WSOCKLIB)
# Checks for GnuTLS
AH_TEMPLATE(WITH_CLIENT_TLS, [Enable support for gnutls in libvncclient])
AC_ARG_WITH(gnutls,
[ --without-gnutls disable support for gnutls],,)
AC_ARG_WITH(client-tls,
[ --without-client-tls disable support for gnutls in libvncclient],,)
if test "x$with_gnutls" != "xno"; then
PKG_CHECK_MODULES(GNUTLS, gnutls >= 2.8.0, , with_client_tls=no)
CFLAGS="$CFLAGS $GNUTLS_CFLAGS"
LIBS="$LIBS $GNUTLS_LIBS"
if test "x$with_client_tls" != "xno"; then
AC_DEFINE(WITH_CLIENT_TLS)
fi
fi
# Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdlib.h string.h sys/socket.h sys/time.h sys/timeb.h syslog.h unistd.h])
......
INCLUDES = -I$(top_srcdir)
libvncclient_la_SOURCES=cursor.c listen.c rfbproto.c sockets.c vncviewer.c minilzo.c
libvncclient_la_SOURCES=cursor.c listen.c rfbproto.c sockets.c vncviewer.c minilzo.c tls.c
noinst_HEADERS=lzoconf.h minilzo.h
noinst_HEADERS=lzoconf.h 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
EXTRA_DIST=corre.c hextile.c rre.c tight.c zlib.c zrle.c ultra.c tls.c
$(libvncclient_la_OBJECTS): ../rfb/rfbclient.h
......
......@@ -53,6 +53,7 @@
#include <time.h>
#include "minilzo.h"
#include "tls.h"
/*
* rfbClientLog prints a time-stamped message to the log file (stderr).
......@@ -415,6 +416,7 @@ ConnectToRFBServer(rfbClient* client,const char *hostname, int port)
}
extern void rfbClientEncryptBytes(unsigned char* bytes, char* passwd);
extern void rfbClientEncryptBytes2(unsigned char *where, const int length, unsigned char *key);
rfbBool
rfbHandleAuthResult(rfbClient* client)
......@@ -456,6 +458,265 @@ 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(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;
}
if (count>sizeof(tAuth))
{
rfbClientLog("%d security types are too many; maximum is %d\n", count, sizeof(tAuth));
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 || tAuth[loop]==rfbMSLogon ||
(!subAuth && (tAuth[loop]==rfbTLS || tAuth[loop]==rfbVeNCrypt)))
{
flag++;
authScheme=tAuth[loop];
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;
}
/*
* InitialiseRFBConnection.
......@@ -466,11 +727,8 @@ InitialiseRFBConnection(rfbClient* client)
{
rfbProtocolVersionMsg pv;
int major,minor;
uint32_t authScheme, reasonLen;
char *reason;
uint8_t challenge[CHALLENGESIZE];
char *passwd=NULL;
int i;
uint32_t authScheme;
uint32_t subAuthScheme;
rfbClientInitMsg ci;
/* if the connection is immediately closed, don't report anything, so
......@@ -530,64 +788,7 @@ InitialiseRFBConnection(rfbClient* client)
/* 3.7 and onwards sends a # of security types first */
if (client->major==3 && client->minor > 6)
{
uint8_t count=0;
uint8_t loop=0;
uint8_t flag=0;
uint8_t tAuth[256];
char buf1[500],buf2[10];
if (!ReadFromRFBServer(client, (char *)&count, 1)) return FALSE;
if (count==0)
{
rfbClientLog("List of security types is ZERO, expecting an error to follow\n");
/* 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;
}
if (count>sizeof(tAuth))
{
rfbClientLog("%d security types are too many; maximum is %d\n", count, sizeof(tAuth));
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==0) && ((tAuth[loop]==rfbVncAuth) || (tAuth[loop]==rfbNoAuth)))
{
flag++;
authScheme=tAuth[loop];
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;
}
if (!ReadSupportedSecurityType(client, &authScheme, FALSE)) return FALSE;
}
else
{
......@@ -596,19 +797,12 @@ InitialiseRFBConnection(rfbClient* client)
}
rfbClientLog("Selected Security Scheme %d\n", authScheme);
client->authScheme = authScheme;
switch (authScheme) {
case rfbConnFailed:
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);
ReadReason(client);
return FALSE;
case rfbNoAuth:
......@@ -621,33 +815,71 @@ InitialiseRFBConnection(rfbClient* client)
break;
case rfbVncAuth:
if (!ReadFromRFBServer(client, (char *)challenge, CHALLENGESIZE)) return FALSE;
if (!HandleVncAuth(client)) return FALSE;
break;
if (client->serverPort!=-1) { /* if not playing a vncrec file */
if (client->GetPassword)
passwd = client->GetPassword(client);
case rfbMSLogon:
if (!HandleMSLogonAuth(client)) return FALSE;
break;
if ((!passwd) || (strlen(passwd) == 0)) {
rfbClientLog("Reading password failed\n");
case rfbTLS:
if (!HandleAnonTLSAuth(client)) return FALSE;
/* After the TLS session is established, sub auth types are expected.
* Note that all following reading/writing are through the TLS session from here.
*/
if (!ReadSupportedSecurityType(client, &subAuthScheme, TRUE)) return FALSE;
client->subAuthScheme = subAuthScheme;
switch (subAuthScheme) {
case rfbConnFailed:
ReadReason(client);
return FALSE;
}
if (strlen(passwd) > 8) {
passwd[8] = '\0';
}
rfbClientEncryptBytes(challenge, passwd);
case rfbNoAuth:
rfbClientLog("No sub authentication needed\n");
if (!rfbHandleAuthResult(client)) return FALSE;
break;
/* Lose the password from memory */
for (i = strlen(passwd); i >= 0; i--) {
passwd[i] = '\0';
}
free(passwd);
case rfbVncAuth:
if (!HandleVncAuth(client)) return FALSE;
break;
if (!WriteToRFBServer(client, (char *)challenge, CHALLENGESIZE)) return FALSE;
default:
rfbClientLog("Unknown sub authentication scheme from VNC server: %d\n",
(int)subAuthScheme);
return FALSE;
}
break;
case rfbVeNCrypt:
if (!HandleVeNCryptAuth(client)) return FALSE;
switch (client->subAuthScheme) {
case rfbVeNCryptTLSNone:
case rfbVeNCryptX509None:
rfbClientLog("No sub authentication needed\n");
if (!rfbHandleAuthResult(client)) return FALSE;
break;
case rfbVeNCryptTLSVNC:
case rfbVeNCryptX509VNC:
if (!HandleVncAuth(client)) return FALSE;
break;
case rfbVeNCryptTLSPlain:
case rfbVeNCryptX509Plain:
if (!HandlePlainAuth(client)) return FALSE;
break;
default:
rfbClientLog("Unknown sub authentication scheme from VNC server: %d\n",
client->subAuthScheme);
return FALSE;
}
/* Handle the SecurityResult message */
if (!rfbHandleAuthResult(client)) return FALSE;
break;
default:
......@@ -1769,6 +2001,7 @@ PrintPixelFormat(rfbPixelFormat *format)
/* avoid name clashes with LibVNCServer */
#define rfbEncryptBytes rfbClientEncryptBytes
#define rfbEncryptBytes2 rfbClientEncryptBytes2
#define rfbDes rfbClientDes
#define rfbDesKey rfbClientDesKey
#define rfbUseKey rfbClientUseKey
......
......@@ -44,6 +44,7 @@
#include <arpa/inet.h>
#include <netdb.h>
#endif
#include "tls.h"
void PrintInHex(char *buf, int len);
......@@ -128,7 +129,16 @@ ReadFromRFBServer(rfbClient* client, char *out, unsigned int n)
if (n <= RFB_BUF_SIZE) {
while (client->buffered < n) {
int i = read(client->sock, client->buf + client->buffered, RFB_BUF_SIZE - client->buffered);
int i;
#ifdef LIBVNCSERVER_WITH_CLIENT_TLS
if (client->tlsSession) {
i = ReadFromTLS(client, client->buf + client->buffered, RFB_BUF_SIZE - client->buffered);
} else {
#endif
i = read(client->sock, client->buf + client->buffered, RFB_BUF_SIZE - client->buffered);
#ifdef LIBVNCSERVER_WITH_CLIENT_TLS
}
#endif
if (i <= 0) {
if (i < 0) {
#ifdef WIN32
......@@ -160,7 +170,16 @@ ReadFromRFBServer(rfbClient* client, char *out, unsigned int n)
} else {
while (n > 0) {
int i = read(client->sock, out, n);
int i;
#ifdef LIBVNCSERVER_WITH_CLIENT_TLS
if (client->tlsSession) {
i = ReadFromTLS(client, out, n);
} else {
#endif
i = read(client->sock, out, n);
#ifdef LIBVNCSERVER_WITH_CLIENT_TLS
}
#endif
if (i <= 0) {
if (i < 0) {
#ifdef WIN32
......@@ -214,6 +233,16 @@ WriteToRFBServer(rfbClient* client, char *buf, int n)
if (client->serverPort==-1)
return TRUE; /* vncrec playing */
#ifdef LIBVNCSERVER_WITH_CLIENT_TLS
if (client->tlsSession) {
/* WriteToTLS() will guarantee either everything is written, or error/eof returns */
i = WriteToTLS(client, buf, n);
if (i <= 0) return FALSE;
return TRUE;
}
#endif
while (i < n) {
j = write(client->sock, buf + i, (n - i));
if (j <= 0) {
......
/*
* Copyright (C) 2009 Vic Lee.
*
* 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 <rfb/rfbclient.h>
#include <errno.h>
#include "tls.h"
#ifdef LIBVNCSERVER_WITH_CLIENT_TLS
static const int rfbCertTypePriority[] = { GNUTLS_CRT_X509, 0 };
static const int rfbProtoPriority[]= { GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 };
static const int rfbKXPriority[] = {GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0};
static const int rfbKXAnon[] = {GNUTLS_KX_ANON_DH, 0};
#define DH_BITS 1024
static gnutls_dh_params_t rfbDHParams;
static rfbBool rfbTLSInitialized = FALSE;
static rfbBool
InitializeTLS(void)
{
int ret;
if (rfbTLSInitialized) return TRUE;
if ((ret = gnutls_global_init()) < 0 ||
(ret = gnutls_dh_params_init(&rfbDHParams)) < 0 ||
(ret = gnutls_dh_params_generate2(rfbDHParams, DH_BITS)) < 0)
{
rfbClientLog("Failed to initialized GnuTLS: %s.\n", gnutls_strerror(ret));
return FALSE;
}
rfbClientLog("GnuTLS initialized.\n");
rfbTLSInitialized = TRUE;
return TRUE;
}
static ssize_t
PushTLS(gnutls_transport_ptr_t transport, const void *data, size_t len)
{
rfbClient *client = (rfbClient*)transport;
int ret;
while (1)
{
ret = write(client->sock, data, len);
if (ret < 0)
{
if (errno == EINTR) continue;
return -1;
}
return ret;
}
}
static ssize_t
PullTLS(gnutls_transport_ptr_t transport, void *data, size_t len)
{
rfbClient *client = (rfbClient*)transport;
int ret;
while (1)
{
ret = read(client->sock, data, len);
if (ret < 0)
{
if (errno == EINTR) continue;
return -1;
}
return ret;
}
}
static rfbBool
InitializeTLSSession(rfbClient* client, rfbBool anonTLS)
{
int ret;
if (client->tlsSession) return TRUE;
if ((ret = gnutls_init(&client->tlsSession, GNUTLS_CLIENT)) < 0)
{
rfbClientLog("Failed to initialized TLS session: %s.\n", gnutls_strerror(ret));
return FALSE;
}
if ((ret = gnutls_set_default_priority(client->tlsSession)) < 0 ||
(ret = gnutls_kx_set_priority(client->tlsSession, anonTLS ? rfbKXAnon : rfbKXPriority)) < 0 ||
(ret = gnutls_certificate_type_set_priority(client->tlsSession, rfbCertTypePriority)) < 0 ||
(ret = gnutls_protocol_set_priority(client->tlsSession, rfbProtoPriority)) < 0)
{
FreeTLS(client);
rfbClientLog("Failed to set TLS priority: %s.\n", gnutls_strerror(ret));
return FALSE;
}
gnutls_transport_set_ptr(client->tlsSession, (gnutls_transport_ptr_t)client);
gnutls_transport_set_push_function(client->tlsSession, PushTLS);
gnutls_transport_set_pull_function(client->tlsSession, PullTLS);
rfbClientLog("TLS session initialized.\n");
return TRUE;
}
static rfbBool
SetTLSAnonCredential(rfbClient* client)
{
gnutls_anon_client_credentials anonCred;
int ret;
if ((ret = gnutls_anon_allocate_client_credentials(&anonCred)) < 0 ||
(ret = gnutls_credentials_set(client->tlsSession, GNUTLS_CRD_ANON, anonCred)) < 0)
{
FreeTLS(client);
rfbClientLog("Failed to create anonymous credentials: %s", gnutls_strerror(ret));
return FALSE;
}
rfbClientLog("TLS anonymous credential created.\n");
return TRUE;
}
static rfbBool
HandshakeTLS(rfbClient* client)
{
int timeout = 15;
int ret;
while (timeout > 0 && (ret = gnutls_handshake(client->tlsSession)) < 0)
{
if (!gnutls_error_is_fatal(ret))
{
rfbClientLog("TLS handshake blocking.\n");
sleep(1);
timeout--;
continue;
}
rfbClientLog("TLS handshake failed: %s.\n", gnutls_strerror(ret));
FreeTLS(client);
return FALSE;
}
if (timeout <= 0)
{
rfbClientLog("TLS handshake timeout.\n");
FreeTLS(client);
return FALSE;
}
rfbClientLog("TLS handshake done.\n");
return TRUE;
}
/* VeNCrypt sub auth. 1 byte auth count, followed by count * 4 byte integers */
static rfbBool
ReadVeNCryptSecurityType(rfbClient* client, uint32_t *result)
{
uint8_t count=0;
uint8_t loop=0;
uint8_t flag=0;
uint32_t tAuth[256], t;
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. Giving up.\n");
return FALSE;
}
if (count>sizeof(tAuth))
{
rfbClientLog("%d security types are too many; maximum is %d\n", count, sizeof(tAuth));
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], 4)) return FALSE;
t=rfbClientSwap32IfLE(tAuth[loop]);
rfbClientLog("%d) Received security type %d\n", loop, t);
if (flag) continue;
if (t==rfbVeNCryptTLSNone ||
t==rfbVeNCryptTLSVNC ||
t==rfbVeNCryptTLSPlain ||
t==rfbVeNCryptX509None ||
t==rfbVeNCryptX509VNC ||
t==rfbVeNCryptX509Plain)
{
flag++;
authScheme=t;
rfbClientLog("Selecting security type %d (%d/%d in the list)\n", authScheme, loop, count);
/* send back 4 bytes (in original byte order!) indicating which security type to use */
if (!WriteToRFBServer(client, (char *)&tAuth[loop], 4)) return FALSE;
}
tAuth[loop]=t;
}
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 VeNCrypt authentication scheme from VNC server: %s\n",
buf1);
return FALSE;
}
*result = authScheme;
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);
}
static gnutls_certificate_credentials_t
CreateX509CertCredential(rfbCredential *cred)
{
gnutls_certificate_credentials_t x509_cred;
int ret;
if (!cred->x509Credential.x509CACertFile)
{
rfbClientLog("No CA certificate provided.\n");
return NULL;
}
if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0)
{
rfbClientLog("Cannot allocate credentials: %s.\n", gnutls_strerror(ret));
return NULL;
}
if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
cred->x509Credential.x509CACertFile, GNUTLS_X509_FMT_PEM)) < 0)
{
rfbClientLog("Cannot load CA credentials: %s.\n", gnutls_strerror(ret));
gnutls_certificate_free_credentials (x509_cred);
return NULL;
}
if (cred->x509Credential.x509ClientCertFile && cred->x509Credential.x509ClientKeyFile)
{
if ((ret = gnutls_certificate_set_x509_key_file(x509_cred,
cred->x509Credential.x509ClientCertFile, cred->x509Credential.x509ClientKeyFile,
GNUTLS_X509_FMT_PEM)) < 0)
{
rfbClientLog("Cannot load client certificate or key: %s.\n", gnutls_strerror(ret));
gnutls_certificate_free_credentials (x509_cred);
return NULL;
}
} else
{
rfbClientLog("No client certificate or key provided.\n");
}
if (cred->x509Credential.x509CACrlFile)
{
if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
cred->x509Credential.x509CACrlFile, GNUTLS_X509_FMT_PEM)) < 0)
{
rfbClientLog("Cannot load CRL: %s.\n", gnutls_strerror(ret));
gnutls_certificate_free_credentials (x509_cred);
return NULL;
}
} else
{
rfbClientLog("No CRL provided.\n");
}
gnutls_certificate_set_dh_params (x509_cred, rfbDHParams);
return x509_cred;
}
#endif
rfbBool
HandleAnonTLSAuth(rfbClient* client)
{
#ifdef LIBVNCSERVER_WITH_CLIENT_TLS
if (!InitializeTLS() || !InitializeTLSSession(client, TRUE)) return FALSE;
if (!SetTLSAnonCredential(client)) return FALSE;
if (!HandshakeTLS(client)) return FALSE;
return TRUE;
#else
rfbClientLog("TLS is not supported.\n");
return FALSE;
#endif
}
rfbBool
HandleVeNCryptAuth(rfbClient* client)
{
#ifdef LIBVNCSERVER_WITH_CLIENT_TLS
uint8_t major, minor, status;
uint32_t authScheme;
rfbBool anonTLS;
gnutls_certificate_credentials_t x509_cred = NULL;
int ret;
if (!InitializeTLS()) return FALSE;
/* Read VeNCrypt version */
if (!ReadFromRFBServer(client, (char *)&major, 1) ||
!ReadFromRFBServer(client, (char *)&minor, 1))
{
return FALSE;
}
rfbClientLog("Got VeNCrypt version %d.%d from server.\n", (int)major, (int)minor);
if (major != 0 && minor != 2)
{
rfbClientLog("Unsupported VeNCrypt version.\n");
return FALSE;
}
if (!WriteToRFBServer(client, (char *)&major, 1) ||
!WriteToRFBServer(client, (char *)&minor, 1) ||
!ReadFromRFBServer(client, (char *)&status, 1))
{
return FALSE;
}
if (status != 0)
{
rfbClientLog("Server refused VeNCrypt version %d.%d.\n", (int)major, (int)minor);
return FALSE;
}
if (!ReadVeNCryptSecurityType(client, &authScheme)) return FALSE;
if (!ReadFromRFBServer(client, (char *)&status, 1) || status != 1)
{
rfbClientLog("Server refused VeNCrypt authentication %d (%d).\n", authScheme, (int)status);
return FALSE;
}
client->subAuthScheme = authScheme;
/* Some VeNCrypt security types are anonymous TLS, others are X509 */
switch (authScheme)
{
case rfbVeNCryptTLSNone:
case rfbVeNCryptTLSVNC:
case rfbVeNCryptTLSPlain:
anonTLS = TRUE;
break;
default:
anonTLS = FALSE;
break;
}
/* Get X509 Credentials if it's not anonymous */
if (!anonTLS)
{
rfbCredential *cred;
if (!client->GetCredential)
{
rfbClientLog("GetCredential callback is not set.\n");
return FALSE;
}
cred = client->GetCredential(client, rfbCredentialTypeX509);
if (!cred)
{
rfbClientLog("Reading credential failed\n");
return FALSE;
}
x509_cred = CreateX509CertCredential(cred);
FreeX509Credential(cred);
if (!x509_cred) return FALSE;
}
/* Start up the TLS session */
if (!InitializeTLSSession(client, anonTLS)) return FALSE;
if (anonTLS)
{
if (!SetTLSAnonCredential(client)) return FALSE;
}
else
{
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 */
/* We are done here. The caller should continue with client->subAuthScheme
* to do actual sub authentication.
*/
return TRUE;
#else
rfbClientLog("TLS is not supported.\n");
return FALSE;
#endif
}
int
ReadFromTLS(rfbClient* client, char *out, unsigned int n)
{
#ifdef LIBVNCSERVER_WITH_CLIENT_TLS
ssize_t ret;
ret = gnutls_record_recv(client->tlsSession, out, n);
if (ret >= 0) return ret;
if (ret == GNUTLS_E_REHANDSHAKE || ret == GNUTLS_E_AGAIN)
{
errno = EAGAIN;
} else
{
rfbClientLog("Error reading from TLS: %s.\n", gnutls_strerror(ret));
errno = EINTR;
}
return -1;
#else
rfbClientLog("TLS is not supported.\n");
errno = EINTR;
return -1;
#endif
}
int
WriteToTLS(rfbClient* client, char *buf, unsigned int n)
{
#ifdef LIBVNCSERVER_WITH_CLIENT_TLS
unsigned int offset = 0;
ssize_t ret;
while (offset < n)
{
ret = gnutls_record_send(client->tlsSession, buf+offset, (size_t)(n-offset));
if (ret == 0) continue;
if (ret < 0)
{
if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) continue;
rfbClientLog("Error writing to TLS: %s.\n", gnutls_strerror(ret));
return -1;
}
offset += (unsigned int)ret;
}
return offset;
#else
rfbClientLog("TLS is not supported.\n");
errno = EINTR;
return -1;
#endif
}
void FreeTLS(rfbClient* client)
{
#ifdef LIBVNCSERVER_WITH_CLIENT_TLS
if (client->tlsSession)
{
gnutls_deinit(client->tlsSession);
client->tlsSession = NULL;
}
#endif
}
#ifndef TLS_H
#define TLS_H
/*
* Copyright (C) 2009 Vic Lee.
*
* 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.
*/
/* Handle Anonymous TLS Authentication (18) with the server.
* After authentication, client->tlsSession will be set.
*/
rfbBool HandleAnonTLSAuth(rfbClient* client);
/* Handle VeNCrypt Authentication (19) with the server.
* The callback function GetX509Credential will be called.
* After authentication, client->tlsSession will be set.
*/
rfbBool HandleVeNCryptAuth(rfbClient* client);
/* Read desired bytes from TLS session.
* It's a wrapper function over gnutls_record_recv() and return values
* are same as read(), that is, >0 for actual bytes read, 0 for EOF,
* or EAGAIN, EINTR.
* This should be a non-blocking call. Blocking is handled in sockets.c.
*/
int ReadFromTLS(rfbClient* client, char *out, unsigned int n);
/* Write desired bytes to TLS session.
* 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);
/* Free TLS resources */
void FreeTLS(rfbClient* client);
#endif /* TLS_H */
......@@ -30,6 +30,7 @@
#include <string.h>
#include <time.h>
#include <rfb/rfbclient.h>
#include "tls.h"
static void Dummy(rfbClient* client) {
}
......@@ -181,6 +182,13 @@ rfbClient* rfbGetClient(int bitsPerSample,int samplesPerPixel,
client->CurrentKeyboardLedState = 0;
client->HandleKeyboardLedState = (HandleKeyboardLedStateProc)DummyPoint;
client->authScheme = 0;
client->subAuthScheme = 0;
client->GetCredential = NULL;
#ifdef LIBVNCSERVER_WITH_CLIENT_TLS
client->tlsSession = NULL;
#endif
return client;
}
......@@ -323,6 +331,7 @@ void rfbClientCleanup(rfbClient* client) {
#endif
#endif
FreeTLS(client);
if (client->sock > 0)
close(client->sock);
free(client->desktopName);
......
......@@ -191,3 +191,18 @@ rfbEncryptBytes(unsigned char *bytes, char *passwd)
rfbDes(bytes+i, bytes+i);
}
}
void
rfbEncryptBytes2(unsigned char *where, const int length, unsigned char *key) {
int i, j;
rfbDesKey(key, EN0);
for (i = 0; i< 8; i++)
where[i] ^= key[i];
rfbDes(where, where);
for (i = 8; i < length; i += 8) {
for (j = 0; j < 8; j++)
where[i + j] ^= where[i + j - 8];
rfbDes(where + i, where + i);
}
}
......@@ -33,6 +33,9 @@
#include <unistd.h>
#include <rfb/rfbproto.h>
#include <rfb/keysym.h>
#ifdef LIBVNCSERVER_WITH_CLIENT_TLS
#include <gnutls/gnutls.h>
#endif
#define rfbClientSwap16IfLE(s) \
(*(char *)&client->endianTest ? ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) : (s))
......@@ -43,6 +46,16 @@
(((l) & 0x0000ff00) << 8) | \
(((l) & 0x000000ff) << 24)) : (l))
#define rfbClientSwap64IfLE(l) \
(*(char *)&client->endianTest ? ((((l) & 0xff00000000000000ULL) >> 56) | \
(((l) & 0x00ff000000000000ULL) >> 40) | \
(((l) & 0x0000ff0000000000ULL) >> 24) | \
(((l) & 0x000000ff00000000ULL) >> 8) | \
(((l) & 0x00000000ff000000ULL) << 8) | \
(((l) & 0x0000000000ff0000ULL) << 24) | \
(((l) & 0x000000000000ff00ULL) << 40) | \
(((l) & 0x00000000000000ffULL) << 56)) : (l))
#define FLASH_PORT_OFFSET 5400
#define LISTEN_PORT_OFFSET 5500
#define TUNNEL_PORT_OFFSET 5500
......@@ -98,6 +111,27 @@ typedef struct {
int scaleSetting; /* 0 means no scale set, else 1/scaleSetting */
} AppData;
/* For GetCredentialProc callback function to return */
typedef union _rfbCredential
{
/* X509 (VeNCrypt) */
struct
{
char *x509CACertFile;
char *x509CACrlFile;
char *x509ClientCertFile;
char *x509ClientKeyFile;
} x509Credential;
/* Plain (VeNCrypt), MSLogon (UltraVNC) */
struct
{
char *username;
char *password;
} userCredential;
} rfbCredential;
#define rfbCredentialTypeX509 1
#define rfbCredentialTypeUser 2
struct _rfbClient;
......@@ -109,6 +143,7 @@ typedef void (*SoftCursorUnlockScreenProc)(struct _rfbClient* client);
typedef void (*GotFrameBufferUpdateProc)(struct _rfbClient* client, int x, int y, int w, int h);
typedef void (*FinishedFrameBufferUpdateProc)(struct _rfbClient* client);
typedef char* (*GetPasswordProc)(struct _rfbClient* client);
typedef rfbCredential* (*GetCredentialProc)(struct _rfbClient* client, int credentialType);
typedef rfbBool (*MallocFrameBufferProc)(struct _rfbClient* client);
typedef void (*GotXCutTextProc)(struct _rfbClient* client, const char *text, int textlen);
typedef void (*BellProc)(struct _rfbClient* client);
......@@ -254,6 +289,22 @@ typedef struct _rfbClient {
/* negotiated protocol version */
int major, minor;
/* The selected security types */
uint32_t authScheme, subAuthScheme;
#ifdef LIBVNCSERVER_WITH_CLIENT_TLS
/* The TLS session for Anonymous TLS and VeNCrypt */
gnutls_session_t tlsSession;
#endif
/* To support security types that requires user input (except VNC password
* authentication), for example VeNCrypt and MSLogon, this callback function
* must be set before the authentication. Otherwise, it implicates that the
* caller application does not support it and related security types should
* be bypassed.
*/
GetCredentialProc GetCredential;
} rfbClient;
/* cursor.c */
......
......@@ -264,6 +264,18 @@ typedef char rfbProtocolVersionMsg[13]; /* allow extra byte for null */
#define rfbTight 16
#define rfbUltra 17
#define rfbTLS 18
#define rfbVeNCrypt 19
#define rfbMSLogon 0xfffffffa
#define rfbVeNCryptPlain 256
#define rfbVeNCryptTLSNone 257
#define rfbVeNCryptTLSVNC 258
#define rfbVeNCryptTLSPlain 259
#define rfbVeNCryptX509None 260
#define rfbVeNCryptX509VNC 261
#define rfbVeNCryptX509Plain 262
#define rfbVeNCryptX509SASL 263
#define rfbVeNCryptTLSSASL 264
/*
* rfbConnFailed: For some reason the connection failed (e.g. the server
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment