diff options
author | Vic Lee <llyzs@163.com> | 2009-10-01 20:22:36 +0800 |
---|---|---|
committer | Johannes Schindelin <johannes.schindelin@gmx.de> | 2009-10-02 11:52:05 +0200 |
commit | 58a8df6ff2bffa46d96b057603c93d824f1c8591 (patch) | |
tree | 22b343f454cd04530de84d42320aa1d390a602ea | |
parent | 0c061f2a2757384fb4f43c2462f649a1ba5a6c9e (diff) | |
download | libvncserver-58a8df6ff2bffa46d96b057603c93d824f1c8591.zip libvncserver-58a8df6ff2bffa46d96b057603c93d824f1c8591.tar.gz |
Add anonymous TLS support in libvncclient
Signed-off-by: Vic Lee <llyzs@163.com>
-rw-r--r-- | configure.ac | 16 | ||||
-rw-r--r-- | libvncclient/Makefile.am | 6 | ||||
-rw-r--r-- | libvncclient/rfbproto.c | 234 | ||||
-rw-r--r-- | libvncclient/sockets.c | 33 | ||||
-rw-r--r-- | libvncclient/tls.c | 275 | ||||
-rw-r--r-- | libvncclient/tls.h | 51 | ||||
-rw-r--r-- | libvncclient/vncviewer.c | 9 | ||||
-rw-r--r-- | rfb/rfbclient.h | 38 | ||||
-rw-r--r-- | rfb/rfbproto.h | 1 |
9 files changed, 567 insertions, 96 deletions
diff --git a/configure.ac b/configure.ac index 65e258c..7b45157 100644 --- a/configure.ac +++ b/configure.ac @@ -667,6 +667,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) + 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]) diff --git a/libvncclient/Makefile.am b/libvncclient/Makefile.am index 24dc8cf..9f42fd3 100644 --- a/libvncclient/Makefile.am +++ b/libvncclient/Makefile.am @@ -1,12 +1,12 @@ 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 diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index fb724f5..1a71f4f 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -53,6 +53,7 @@ #include <time.h> #include "minilzo.h" +#include "tls.h" /* * rfbClientLog prints a time-stamped message to the log file (stderr). @@ -454,6 +455,119 @@ 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 || + (!subAuth && tAuth[loop]==rfbTLS)) + { + 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; +} /* * InitialiseRFBConnection. @@ -464,11 +578,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 @@ -528,64 +639,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 { @@ -594,19 +648,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: @@ -619,33 +666,38 @@ 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 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; - if ((!passwd) || (strlen(passwd) == 0)) { - rfbClientLog("Reading password failed\n"); + 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; } - /* Handle the SecurityResult message */ - if (!rfbHandleAuthResult(client)) return FALSE; break; default: diff --git a/libvncclient/sockets.c b/libvncclient/sockets.c index 5cfc743..4dd8165 100644 --- a/libvncclient/sockets.c +++ b/libvncclient/sockets.c @@ -43,6 +43,7 @@ #include <arpa/inet.h> #include <netdb.h> #endif +#include "tls.h" void PrintInHex(char *buf, int len); @@ -127,7 +128,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 @@ -159,7 +169,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 @@ -213,6 +232,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) { diff --git a/libvncclient/tls.c b/libvncclient/tls.c new file mode 100644 index 0000000..b8d2d1f --- /dev/null +++ b/libvncclient/tls.c @@ -0,0 +1,275 @@ +/* + * 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; +} + +#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 + int ret; + + if (!InitializeTLS() || !InitializeTLSSession(client, FALSE)) return FALSE; + + /* TODO: read VeNCrypt version, etc */ + /* TODO: call GetCredential and set to TLS session */ + + if (!HandshakeTLS(client)) return FALSE; + + /* TODO: validate certificate */ + + 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 +} diff --git a/libvncclient/tls.h b/libvncclient/tls.h new file mode 100644 index 0000000..48d159b --- /dev/null +++ b/libvncclient/tls.h @@ -0,0 +1,51 @@ +#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 */ diff --git a/libvncclient/vncviewer.c b/libvncclient/vncviewer.c index a5f2f89..c2c7286 100644 --- a/libvncclient/vncviewer.c +++ b/libvncclient/vncviewer.c @@ -30,6 +30,7 @@ #include <string.h> #include <time.h> #include <rfb/rfbclient.h> +#include "tls.h" static void Dummy(rfbClient* client) { } @@ -179,6 +180,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; } @@ -318,6 +326,7 @@ void rfbClientCleanup(rfbClient* client) { #endif #endif + FreeTLS(client); if (client->sock > 0) close(client->sock); free(client->desktopName); diff --git a/rfb/rfbclient.h b/rfb/rfbclient.h index 07da7df..b3a0d87 100644 --- a/rfb/rfbclient.h +++ b/rfb/rfbclient.h @@ -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)) @@ -98,6 +101,24 @@ typedef struct { int scaleSetting; /* 0 means no scale set, else 1/scaleSetting */ } AppData; +/* For GetCredentialProc callback function to return */ +typedef union _rfbCredential +{ + /* VeNCrypt */ + struct + { + char *x509CACertFile; + char *x509CACrlFile; + char *x509ClientCertFile; + char *x509ClientKeyFile; + } x509Credential; + /* MSLogon */ + struct + { + char *username; + char *password; + } userCredential; +} rfbCredential; struct _rfbClient; @@ -108,6 +129,7 @@ typedef void (*SoftCursorLockAreaProc)(struct _rfbClient* client, int x, int y, typedef void (*SoftCursorUnlockScreenProc)(struct _rfbClient* client); typedef void (*GotFrameBufferUpdateProc)(struct _rfbClient* client, int x, int y, int w, int h); typedef char* (*GetPasswordProc)(struct _rfbClient* client); +typedef rfbCredential* (*GetCredentialProc)(struct _rfbClient* client, uint8_t securityType); typedef rfbBool (*MallocFrameBufferProc)(struct _rfbClient* client); typedef void (*GotXCutTextProc)(struct _rfbClient* client, const char *text, int textlen); typedef void (*BellProc)(struct _rfbClient* client); @@ -249,6 +271,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 */ diff --git a/rfb/rfbproto.h b/rfb/rfbproto.h index f0660e1..fec6bf7 100644 --- a/rfb/rfbproto.h +++ b/rfb/rfbproto.h @@ -264,6 +264,7 @@ typedef char rfbProtocolVersionMsg[13]; /* allow extra byte for null */ #define rfbTight 16 #define rfbUltra 17 #define rfbTLS 18 +#define rfbVeNCrypt 19 /* * rfbConnFailed: For some reason the connection failed (e.g. the server |