Added SASL authentication support

Added SASL support to OpenSSL
pull/3/head
simon 8 years ago committed by simon
parent c80879ee69
commit cb4e15c1ae

@ -1,8 +1,14 @@
#environment:
# APPVEYOR_RDP_PASSWORD: Pa55word
os: os:
- Visual Studio 2013 - Visual Studio 2013
- Visual Studio 2015 - Visual Studio 2015
#init:
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
install: install:
- mkdir deps - mkdir deps
- cd deps - cd deps
@ -22,13 +28,32 @@ install:
- cmake . -DZLIB_INCLUDE_DIR=..\zlib -DZLIB_LIBRARY=..\zlib\debug\zlibstaticd.lib - cmake . -DZLIB_INCLUDE_DIR=..\zlib -DZLIB_LIBRARY=..\zlib\debug\zlibstaticd.lib
- cmake --build . - cmake --build .
- cd .. - cd ..
# Berkeley DB - required by SASL
- curl -fsSL -o db-4.1.25.tar.gz http://download.oracle.com/berkeley-db/db-4.1.25.tar.gz
- 7z x db-4.1.25.tar.gz -so | 7z x -si -ttar > nul
- move db-4.1.25 db
- cd db\build_win32
- C:\"Program Files (x86)"\"Microsoft Visual Studio 12.0"\Common7\IDE\devenv.exe db_dll.dsp /upgrade
- msbuild /p:Configuration=Release db_dll.vcxproj
- cd ..\..
# Cyrus SASL
- curl -fsSL -o cyrus-sasl-2.1.26.tar.gz ftp://ftp.cyrusimap.org/cyrus-sasl/cyrus-sasl-2.1.26.tar.gz
- 7z x cyrus-sasl-2.1.26.tar.gz -so | 7z x -si -ttar > nul
- move cyrus-sasl-2.1.26 sasl
- cd sasl
- '"%vs120comntools%\VsDevCmd.bat"'
- nmake /f NTMakefile OPENSSL_INCLUDE=c:\OpenSSL-Win32\include OPENSSL_LIBPATH=c:\OpenSSL-Win32\lib DB_INCLUDE=c:\projects\libvncserver\deps\db\build_win32 DB_LIBPATH=c:\projects\libvncserver\deps\db\build_win32\release DB_LIB=libdb41.lib install
- cd ..
# go back to source root # go back to source root
- cd .. - cd ..
build_script: build_script:
- mkdir build - mkdir build
- cd build - cd build
- cmake .. -DZLIB_INCLUDE_DIR=..\deps\zlib -DZLIB_LIBRARY=..\deps\zlib\debug\zlibstaticd.lib -DPNG_PNG_INCLUDE_DIR=..\deps\libpng -DPNG_LIBRARY=..\deps\libpng\debug\libpng16_staticd.lib - cmake .. -DZLIB_INCLUDE_DIR=..\deps\zlib -DZLIB_LIBRARY=..\deps\zlib\debug\zlibstaticd.lib -DPNG_PNG_INCLUDE_DIR=..\deps\libpng -DPNG_LIBRARY=..\deps\libpng\debug\libpng16_staticd.lib -D SASL2_INCLUDE_DIR=c:\cmu\include -D LIBSASL2_LIBRARIES=c:\cmu\lib\libsasl.lib ..
- cmake --build . - cmake --build .
- ctest -C Debug --output-on-failure - ctest -C Debug --output-on-failure
#on_finish:
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))

@ -47,6 +47,7 @@ option(WITH_TIGHTVNC_FILETRANSFER "Enable filetransfer if there is pthreads supp
option(WITH_24BPP "Allow 24 bpp" ON) option(WITH_24BPP "Allow 24 bpp" ON)
option(WITH_IPv6 "Enable IPv6 Support" ON) option(WITH_IPv6 "Enable IPv6 Support" ON)
option(WITH_WEBSOCKETS "Build with websockets support" ON) option(WITH_WEBSOCKETS "Build with websockets support" ON)
option(WITH_SASL "Build with SASL support" ON)
@ -286,6 +287,18 @@ endif(NOT HAVE_LIBVNCSERVER_IN_ADDR_T)
TEST_BIG_ENDIAN(LIBVNCSERVER_WORDS_BIGENDIAN) TEST_BIG_ENDIAN(LIBVNCSERVER_WORDS_BIGENDIAN)
if(WITH_SASL)
find_path(SASL2_INCLUDE_DIR sasl/sasl.h)
find_library(LIBSASL2_LIBRARIES sasl2 libsasl.lib)
endif(WITH_SASL)
if(WITH_SASL AND LIBSASL2_LIBRARIES AND SASL2_INCLUDE_DIR)
message(STATUS "Building with SASL: ${LIBSASL2_LIBRARIES} and ${SASL2_INCLUDE_DIR}")
set(LIBVNCSERVER_HAVE_SASL 1)
set(ADDITIONAL_LIBS ${ADDITIONAL_LIBS} ${LIBSASL2_LIBRARIES})
include_directories(${SASL2_INCLUDE_DIR})
endif(WITH_SASL AND LIBSASL2_LIBRARIES AND SASL2_INCLUDE_DIR)
# TODO: # TODO:
# LIBVNCSERVER_ENOENT_WORKAROUND # LIBVNCSERVER_ENOENT_WORKAROUND
# inline # inline
@ -344,6 +357,13 @@ else()
) )
endif() endif()
if(LIBVNCSERVER_HAVE_SASL)
set(LIBVNCCLIENT_SOURCES
${LIBVNCCLIENT_SOURCES}
${LIBVNCCLIENT_DIR}/rfbsasl.c
)
endif()
if(ZLIB_FOUND) if(ZLIB_FOUND)
add_definitions(-DLIBVNCSERVER_HAVE_LIBZ) add_definitions(-DLIBVNCSERVER_HAVE_LIBZ)
include_directories(${ZLIB_INCLUDE_DIR}) include_directories(${ZLIB_INCLUDE_DIR})

@ -58,12 +58,25 @@ static void SaveFramebufferAsPPM(rfbClient* client, int x, int y, int w, int h)
fclose(f); fclose(f);
} }
char * getuser(rfbClient *client)
{
return strdup("testuser@test");
}
char * getpassword(rfbClient *client)
{
return strdup("Password");
}
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
rfbClient* client = rfbGetClient(8,3,4); rfbClient* client = rfbGetClient(8,3,4);
time_t t=time(NULL); time_t t=time(NULL);
client->GetUser = getuser;
client->GetPassword = getpassword;
if(argc>1 && !strcmp("-print",argv[1])) { if(argc>1 && !strcmp("-print",argv[1])) {
client->GotFrameBufferUpdate = PrintRect; client->GotFrameBufferUpdate = PrintRect;
argv[1]=argv[0]; argv++; argc--; argv[1]=argv[0]; argv++; argc--;

@ -66,6 +66,10 @@
#include <gcrypt.h> #include <gcrypt.h>
#endif #endif
#ifdef LIBVNCSERVER_HAVE_SASL
#include "rfbsasl.h"
#endif /* LIBVNCSERVER_HAVE_SASL */
#include "minilzo.h" #include "minilzo.h"
#include "tls.h" #include "tls.h"
@ -500,6 +504,9 @@ ReadSupportedSecurityType(rfbClient* client, uint32_t *result, rfbBool subAuth)
#if defined(LIBVNCSERVER_HAVE_GNUTLS) || defined(LIBVNCSERVER_HAVE_LIBSSL) #if defined(LIBVNCSERVER_HAVE_GNUTLS) || defined(LIBVNCSERVER_HAVE_LIBSSL)
tAuth[loop]==rfbVeNCrypt || tAuth[loop]==rfbVeNCrypt ||
#endif #endif
#ifdef LIBVNCSERVER_HAVE_SASL
tAuth[loop]==rfbSASL ||
#endif /* LIBVNCSERVER_HAVE_SASL */
(tAuth[loop]==rfbARD && client->GetCredential) || (tAuth[loop]==rfbARD && client->GetCredential) ||
(!subAuth && (tAuth[loop]==rfbTLS || (tAuth[loop]==rfbVeNCrypt && client->GetCredential)))) (!subAuth && (tAuth[loop]==rfbTLS || (tAuth[loop]==rfbVeNCrypt && client->GetCredential))))
{ {
@ -1079,6 +1086,12 @@ InitialiseRFBConnection(rfbClient* client)
if (!HandleVncAuth(client)) return FALSE; if (!HandleVncAuth(client)) return FALSE;
break; break;
#ifdef LIBVNCSERVER_HAVE_SASL
case rfbSASL:
if (!HandleSASLAuth(client)) return FALSE;
break;
#endif /* LIBVNCSERVER_HAVE_SASL */
case rfbMSLogon: case rfbMSLogon:
if (!HandleMSLogonAuth(client)) return FALSE; if (!HandleMSLogonAuth(client)) return FALSE;
break; break;
@ -1117,6 +1130,12 @@ InitialiseRFBConnection(rfbClient* client)
if (!HandleVncAuth(client)) return FALSE; if (!HandleVncAuth(client)) return FALSE;
break; break;
#ifdef LIBVNCSERVER_HAVE_SASL
case rfbSASL:
if (!HandleSASLAuth(client)) return FALSE;
break;
#endif /* LIBVNCSERVER_HAVE_SASL */
default: default:
rfbClientLog("Unknown sub authentication scheme from VNC server: %d\n", rfbClientLog("Unknown sub authentication scheme from VNC server: %d\n",
(int)subAuthScheme); (int)subAuthScheme);
@ -1146,6 +1165,13 @@ InitialiseRFBConnection(rfbClient* client)
if (!HandlePlainAuth(client)) return FALSE; if (!HandlePlainAuth(client)) return FALSE;
break; break;
#ifdef LIBVNCSERVER_HAVE_SASL
case rfbVeNCryptX509SASL:
case rfbVeNCryptTLSSASL:
if (!HandleSASLAuth(client)) return FALSE;
break;
#endif /* LIBVNCSERVER_HAVE_SASL */
default: default:
rfbClientLog("Unknown sub authentication scheme from VNC server: %d\n", rfbClientLog("Unknown sub authentication scheme from VNC server: %d\n",
client->subAuthScheme); client->subAuthScheme);

@ -0,0 +1,579 @@
/*
* The software in this file is derived from the vncconnection.c source file
* from the GTK VNC Widget with modifications by S. Waterman <simon.waterman@zynstra.com>
* for compatibility with libvncserver. The copyright and license
* statements below apply only to this source file and to no other parts of the
* libvncserver library.
*
* GTK VNC Widget
*
* Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
* Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* rfbsasl.c - functions to deal with client side of the SASL protocol.
*/
#ifdef __STRICT_ANSI__
#define _BSD_SOURCE
#define _POSIX_SOURCE
#define _XOPEN_SOURCE 600
#endif
#include <errno.h>
#include <rfb/rfbclient.h>
#ifdef WIN32
#undef SOCKET
#include <winsock2.h>
#define EWOULDBLOCK WSAEWOULDBLOCK
#define socklen_t int
#define close closesocket
#define read(sock,buf,len) recv(sock,buf,len,0)
#define write(sock,buf,len) send(sock,buf,len,0)
#ifdef LIBVNCSERVER_HAVE_WS2TCPIP_H
#undef socklen_t
#include <ws2tcpip.h>
#endif /* LIBVNCSERVER_HAVE_WS2TCPIP_H */
#else /* WIN32 */
#include <arpa/inet.h>
#endif /* WIN32 */
#include "rfbsasl.h"
#include "tls.h"
#ifdef _MSC_VER
# define snprintf _snprintf /* MSVC went straight to the underscored syntax */
#endif
/*
* NB, keep in sync with similar method in qemud/remote.c
*/
static char *vnc_connection_addr_to_string(char *host, int port)
{
char * buf = (char *)malloc(strlen(host) + 7);
sprintf(buf, "%s;%hu", host, port);
return buf;
}
static int log_func(void *context,
int level,
const char *message)
{
rfbClientLog("SASL: %s\n", message);
return SASL_OK;
}
static int user_callback_adapt(void *context,
int id,
const char **result,
unsigned *len)
{
rfbClient* client = (rfbClient *)context;
if (id != SASL_CB_AUTHNAME) {
rfbClientLog("Unrecognized SASL callback ID %d\n", id);
return SASL_FAIL;
}
if (!client->GetUser) {
rfbClientLog("Client user callback not found\n");
return SASL_FAIL;
}
*result = client->GetUser(client);
if (! *result) return SASL_FAIL;
/**len = strlen(*result);*/
return SASL_OK;
}
static int password_callback_adapt(sasl_conn_t *conn,
void * context,
int id,
sasl_secret_t **secret)
{
rfbClient* client = (rfbClient *)context;
char * password;
if (id != SASL_CB_PASS) {
rfbClientLog("Unrecognized SASL callback ID %d\n", id);
return SASL_FAIL;
}
if (client->saslSecret) { /* If we've already got it just return it. */
*secret = client->saslSecret;
return SASL_OK;
}
if (!client->GetPassword) {
rfbClientLog("Client password callback not found\n");
return SASL_FAIL;
}
password = client->GetPassword(client);
if (! password) return SASL_FAIL;
sasl_secret_t *lsec = (sasl_secret_t *)malloc(sizeof(sasl_secret_t) + strlen(password));
if (!lsec) {
rfbClientLog("Could not allocate sasl_secret_t\n");
return SASL_FAIL;
}
strcpy(lsec->data, password);
lsec->len = strlen(password);
client->saslSecret = lsec;
*secret = lsec;
/* Clear client password */
size_t i;
for (i = 0; i < lsec->len; i++) {
password[i] = '\0';
}
free(password);
return SASL_OK;
}
#define SASL_MAX_MECHLIST_LEN 300
#define SASL_MAX_DATA_LEN (1024 * 1024)
/* Perform the SASL authentication process
*/
rfbBool
HandleSASLAuth(rfbClient *client)
{
sasl_conn_t *saslconn = NULL;
sasl_security_properties_t secprops;
const char *clientout;
char *serverin = NULL;
unsigned int clientoutlen, serverinlen;
int err, complete = 0;
char *localAddr = NULL, *remoteAddr = NULL;
const void *val;
sasl_ssf_t ssf;
sasl_callback_t saslcb[] = {
{SASL_CB_LOG, (void *)log_func, NULL},
{SASL_CB_AUTHNAME, client->GetUser ? (void *)user_callback_adapt : NULL, client},
{SASL_CB_PASS, client->GetPassword ? (void *)password_callback_adapt : NULL, client},
{ .id = 0 },
};
sasl_interact_t *interact = NULL;
uint32_t mechlistlen;
char *mechlist;
char *wantmech;
const char *mechname;
rfbBool ret;
client->saslconn = NULL;
/* Sets up the SASL library as a whole */
err = sasl_client_init(NULL);
rfbClientLog("Client initialize SASL authentication %d\n", err);
if (err != SASL_OK) {
rfbClientLog("failed to initialize SASL library: %d (%s)\n",
err, sasl_errstring(err, NULL, NULL));
goto error;
}
/* Get local address in form IPADDR:PORT */
struct sockaddr_storage localAddress;
socklen_t addressLength = sizeof(localAddress);
char buf[INET6_ADDRSTRLEN];
int port;
if (getsockname(client->sock, (struct sockaddr*)&localAddress, &addressLength)) {
rfbClientLog("failed to get local address\n");
goto error;
}
if (localAddress.ss_family == AF_INET) {
struct sockaddr_in *sa_in = (struct sockaddr_in*)&localAddress;
inet_ntop(AF_INET, &(sa_in->sin_addr), buf, INET_ADDRSTRLEN);
port = ntohs(sa_in->sin_port);
localAddr = vnc_connection_addr_to_string(buf, port);
} else if (localAddress.ss_family == AF_INET6) {
struct sockaddr_in6 *sa_in = (struct sockaddr_in6*)&localAddress;
inet_ntop(AF_INET6, &(sa_in->sin6_addr), buf, INET6_ADDRSTRLEN);
port = ntohs(sa_in->sin6_port);
localAddr = vnc_connection_addr_to_string(buf, port);
} else {
rfbClientLog("failed to get local address\n");
goto error;
}
/* Get remote address in form IPADDR:PORT */
remoteAddr = vnc_connection_addr_to_string(client->serverHost, client->serverPort);
rfbClientLog("Client SASL new host:'%s' local:'%s' remote:'%s'\n", client->serverHost, localAddr, remoteAddr);
/* Setup a handle for being a client */
err = sasl_client_new("vnc",
client->serverHost,
localAddr,
remoteAddr,
saslcb,
SASL_SUCCESS_DATA,
&saslconn);
free(localAddr);
free(remoteAddr);
if (err != SASL_OK) {
rfbClientLog("Failed to create SASL client context: %d (%s)\n",
err, sasl_errstring(err, NULL, NULL));
goto error;
}
/* Initialize some connection props we care about */
if (client->tlsSession) {
if (!(ssf = (sasl_ssf_t)GetTLSCipherBits(client))) {
rfbClientLog("%s", "invalid cipher size for TLS session\n");
goto error;
}
rfbClientLog("Setting external SSF %d\n", ssf);
err = sasl_setprop(saslconn, SASL_SSF_EXTERNAL, &ssf);
if (err != SASL_OK) {
rfbClientLog("cannot set external SSF %d (%s)\n",
err, sasl_errstring(err, NULL, NULL));
goto error;
}
}
memset (&secprops, 0, sizeof secprops);
/* If we've got TLS, we don't care about SSF */
secprops.min_ssf = client->tlsSession ? 0 : 56; /* Equiv to DES supported by all Kerberos */
secprops.max_ssf = client->tlsSession ? 0 : 100000; /* Very strong ! AES == 256 */
secprops.maxbufsize = 100000;
/* If we're not TLS, then forbid any anonymous or trivially crackable auth */
secprops.security_flags = client->tlsSession ? 0 :
SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
err = sasl_setprop(saslconn, SASL_SEC_PROPS, &secprops);
if (err != SASL_OK) {
rfbClientLog("cannot set security props %d (%s)\n",
err, sasl_errstring(err, NULL, NULL));
goto error;
}
/* Get the supported mechanisms from the server */
if (!ReadFromRFBServer(client, (char *)&mechlistlen, 4)) {
rfbClientLog("failed to read mechlistlen\n");
goto error;
}
mechlistlen = rfbClientSwap32IfLE(mechlistlen);
rfbClientLog("mechlistlen is %d\n", mechlistlen);
if (mechlistlen > SASL_MAX_MECHLIST_LEN) {
rfbClientLog("mechlistlen %d too long\n", mechlistlen);
goto error;
}
mechlist = malloc(mechlistlen+1);
if (!ReadFromRFBServer(client, mechlist, mechlistlen)) {
free(mechlist);
goto error;
}
mechlist[mechlistlen] = '\0';
/* Allow the client to influence the mechanism selected. */
if (client->GetSASLMechanism) {
wantmech = client->GetSASLMechanism(client, mechlist);
if (wantmech && *wantmech != 0) {
if (strstr(mechlist, wantmech) == NULL) {
rfbClientLog("Client requested SASL mechanism %s not supported by server\n",
wantmech);
free(mechlist);
free(wantmech);
goto error;
} else {
free(mechlist);
mechlist = wantmech;
}
}
}
rfbClientLog("Client start negotiation mechlist '%s'\n", mechlist);
restart:
/* Start the auth negotiation on the client end first */
err = sasl_client_start(saslconn,
mechlist,
&interact,
&clientout,
&clientoutlen,
&mechname);
if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) {
rfbClientLog("Failed to start SASL negotiation: %d (%s)\n",
err, sasl_errdetail(saslconn));
free(mechlist);
mechlist = NULL;
goto error;
}
/* Need to gather some credentials from the client */
if (err == SASL_INTERACT) {
rfbClientLog("User interaction required but not currently supported\n");
goto error;
}
rfbClientLog("Server start negotiation with mech %s. Data %d bytes %p '%s'\n",
mechname, clientoutlen, clientout, clientout);
if (clientoutlen > SASL_MAX_DATA_LEN) {
rfbClientLog("SASL negotiation data too long: %d bytes\n",
clientoutlen);
goto error;
}
/* Send back the chosen mechname */
uint32_t mechnamelen = rfbClientSwap32IfLE(strlen(mechname));
if (!WriteToRFBServer(client, (char *)&mechnamelen, 4)) goto error;
if (!WriteToRFBServer(client, (char *)mechname, strlen(mechname))) goto error;
/* NB, distinction of NULL vs "" is *critical* in SASL */
if (clientout) {
uint32_t colsw = rfbClientSwap32IfLE(clientoutlen + 1);
if (!WriteToRFBServer(client, (char *)&colsw, 4)) goto error;
if (!WriteToRFBServer(client, (char *)clientout, clientoutlen + 1)) goto error;
} else {
uint32_t temp = 0;
if (!WriteToRFBServer(client, (char *)&temp, 4)) goto error;
}
rfbClientLog("%s", "Getting sever start negotiation reply\n");
/* Read the 'START' message reply from server */
if (!ReadFromRFBServer(client, (char *)&serverinlen, 4)) goto error;
serverinlen = rfbClientSwap32IfLE(serverinlen);
if (serverinlen > SASL_MAX_DATA_LEN) {
rfbClientLog("SASL negotiation data too long: %d bytes\n",
serverinlen);
goto error;
}
/* NB, distinction of NULL vs "" is *critical* in SASL */
if (serverinlen) {
serverin = malloc(serverinlen);
if (!ReadFromRFBServer(client, serverin, serverinlen)) goto error;
serverin[serverinlen-1] = '\0';
serverinlen--;
} else {
serverin = NULL;
}
if (!ReadFromRFBServer(client, (char *)&complete, 1)) goto error;
rfbClientLog("Client start result complete: %d. Data %d bytes %p '%s'\n",
complete, serverinlen, serverin, serverin);
/* Loop-the-loop...
* Even if the server has completed, the client must *always* do at least one step
* in this loop to verify the server isn't lying about something. Mutual auth */
for (;;) {
restep:
err = sasl_client_step(saslconn,
serverin,
serverinlen,
&interact,
&clientout,
&clientoutlen);
if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) {
rfbClientLog("Failed SASL step: %d (%s)\n",
err, sasl_errdetail(saslconn));
goto error;
}
/* Need to gather some credentials from the client */
if (err == SASL_INTERACT) {
rfbClientLog("User interaction required but not currently supported\n");
goto error;
}
if (serverin) {
free(serverin);
serverin = NULL;
}
rfbClientLog("Client step result %d. Data %d bytes %p '%s'\n", err, clientoutlen, clientout, clientout);
/* Previous server call showed completion & we're now locally complete too */
if (complete && err == SASL_OK)
break;
/* Not done, prepare to talk with the server for another iteration */
/* NB, distinction of NULL vs "" is *critical* in SASL */
if (clientout) {
uint32_t colsw = rfbClientSwap32IfLE(clientoutlen + 1);
if (!WriteToRFBServer(client, (char *)&colsw, 4)) goto error;
if (!WriteToRFBServer(client, (char *)clientout, clientoutlen + 1)) goto error;
} else {
uint32_t temp = 0;
if (!WriteToRFBServer(client, (char *)&temp, 4)) goto error;
}
rfbClientLog("Server step with %d bytes %p\n", clientoutlen, clientout);
if (!ReadFromRFBServer(client, (char *)&serverinlen, 4)) goto error;
serverinlen = rfbClientSwap32IfLE(serverinlen);
if (serverinlen > SASL_MAX_DATA_LEN) {
rfbClientLog("SASL negotiation data too long: %d bytes\n",
serverinlen);
goto error;
}
/* NB, distinction of NULL vs "" is *critical* in SASL */
if (serverinlen) {
serverin = malloc(serverinlen);
if (!ReadFromRFBServer(client, serverin, serverinlen)) goto error;
serverin[serverinlen-1] = '\0';
serverinlen--;
} else {
serverin = NULL;
}
if (!ReadFromRFBServer(client, (char *)&complete, 1)) goto error;
rfbClientLog("Client step result complete: %d. Data %d bytes %p '%s'\n",
complete, serverinlen, serverin, serverin);
/* This server call shows complete, and earlier client step was OK */
if (complete && err == SASL_OK) {
free(serverin);
serverin = NULL;
break;
}
}
/* Check for suitable SSF if non-TLS */
if (!client->tlsSession) {
err = sasl_getprop(saslconn, SASL_SSF, &val);
if (err != SASL_OK) {
rfbClientLog("cannot query SASL ssf on connection %d (%s)\n",
err, sasl_errstring(err, NULL, NULL));
goto error;
}
ssf = *(const int *)val;
rfbClientLog("SASL SSF value %d\n", ssf);
if (ssf < 56) { /* 56 == DES level, good for Kerberos */
rfbClientLog("negotiation SSF %d was not strong enough\n", ssf);
goto error;
}
}
rfbClientLog("%s", "SASL authentication complete\n");
uint32_t result;
if (!ReadFromRFBServer(client, (char *)&result, 4)) {
rfbClientLog("Failed to read authentication result\n");
goto error;
}
result = rfbClientSwap32IfLE(result);
if (result != 0) {
rfbClientLog("Authentication failure\n");
goto error;
}
rfbClientLog("Authentication successful - switching to SSF\n");
/* This must come *after* check-auth-result, because the former
* is defined to be sent unencrypted, and setting saslconn turns
* on the SSF layer encryption processing */
client->saslconn = saslconn;
/* Clear SASL secret from memory if set - it'll be free'd on dispose */
if (client->saslSecret) {
size_t i;
for (i = 0; i < client->saslSecret->len; i++)
client->saslSecret->data[i] = '\0';
client->saslSecret->len = 0;
}
return TRUE;
error:
if (client->saslSecret) {
size_t i;
for (i = 0; i < client->saslSecret->len; i++)
client->saslSecret->data[i] = '\0';
client->saslSecret->len = 0;
}
if (saslconn)
sasl_dispose(&saslconn);
return FALSE;
}
int
ReadFromSASL(rfbClient* client, char *out, unsigned int n)
{
size_t want;
if (client->saslDecoded == NULL) {
char *encoded;
int encodedLen;
int err, ret;
encodedLen = 8192;
encoded = (char *)malloc(encodedLen);
ret = read(client->sock, encoded, encodedLen);
if (ret < 0) {
free(encoded);
return ret;
}
if (ret == 0) {
free(encoded);
errno = EIO;
return -EIO;
}
err = sasl_decode(client->saslconn, encoded, ret,
&client->saslDecoded, &client->saslDecodedLength);
free(encoded);
if (err != SASL_OK) {
rfbClientLog("Failed to decode SASL data %s\n",
sasl_errstring(err, NULL, NULL));
return -EINVAL;
}
client->saslDecodedOffset = 0;
}
want = client->saslDecodedLength - client->saslDecodedOffset;
if (want > n)
want = n;
memcpy(out,
client->saslDecoded + client->saslDecodedOffset,
want);
client->saslDecodedOffset += want;
if (client->saslDecodedOffset == client->saslDecodedLength) {
client->saslDecodedLength = client->saslDecodedOffset = 0;
client->saslDecoded = NULL;
}
if (!want) {
errno = EAGAIN;
return -EAGAIN;
}
return want;
}

@ -0,0 +1,35 @@
#ifndef RFBSASL_H
#define RFBSASL_H
/*
* Copyright (C) 2017 S. Waterman. All Rights Reserved.
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#include <rfb/rfbclient.h>
/*
* Perform the SASL authentication process
*/
rfbBool HandleSASLAuth(rfbClient *client);
/*
* Read from SASL when the SASL SSF is in use.
*/
int ReadFromSASL(rfbClient* client, char *out, unsigned int n);
#endif /* RFBSASL_H */

@ -59,6 +59,10 @@
#endif #endif
#include "tls.h" #include "tls.h"
#ifdef LIBVNCSERVER_HAVE_SASL
#include "rfbsasl.h"
#endif /* LIBVNCSERVER_HAVE_SASL */
#ifdef _MSC_VER #ifdef _MSC_VER
# define snprintf _snprintf # define snprintf _snprintf
#endif #endif
@ -154,16 +158,24 @@ ReadFromRFBServer(rfbClient* client, char *out, unsigned int n)
while (client->buffered < n) { while (client->buffered < n) {
int i; int i;
if (client->tlsSession) { if (client->tlsSession)
i = ReadFromTLS(client, client->buf + client->buffered, RFB_BUF_SIZE - client->buffered); i = ReadFromTLS(client, client->buf + client->buffered, RFB_BUF_SIZE - client->buffered);
} else { else
#ifdef LIBVNCSERVER_HAVE_SASL
if (client->saslconn)
i = ReadFromSASL(client, client->buf + client->buffered, RFB_BUF_SIZE - client->buffered);
else {
#endif /* LIBVNCSERVER_HAVE_SASL */
i = read(client->sock, client->buf + client->buffered, RFB_BUF_SIZE - client->buffered); i = read(client->sock, client->buf + client->buffered, RFB_BUF_SIZE - client->buffered);
#ifdef WIN32
if (i < 0) errno=WSAGetLastError();
#endif
#ifdef LIBVNCSERVER_HAVE_SASL
} }
#endif
if (i <= 0) { if (i <= 0) {
if (i < 0) { if (i < 0) {
#ifdef WIN32
errno=WSAGetLastError();
#endif
if (errno == EWOULDBLOCK || errno == EAGAIN) { if (errno == EWOULDBLOCK || errno == EAGAIN) {
/* TODO: /* TODO:
ProcessXtEvents(); ProcessXtEvents();
@ -192,11 +204,15 @@ ReadFromRFBServer(rfbClient* client, char *out, unsigned int n)
while (n > 0) { while (n > 0) {
int i; int i;
if (client->tlsSession) { if (client->tlsSession)
i = ReadFromTLS(client, out, n); i = ReadFromTLS(client, out, n);
} else { else
#ifdef LIBVNCSERVER_HAVE_SASL
if (client->saslconn)
i = ReadFromSASL(client, out, n);
else
#endif
i = read(client->sock, out, n); i = read(client->sock, out, n);
}
if (i <= 0) { if (i <= 0) {
if (i < 0) { if (i < 0) {
@ -248,6 +264,12 @@ WriteToRFBServer(rfbClient* client, char *buf, int n)
fd_set fds; fd_set fds;
int i = 0; int i = 0;
int j; int j;
const char *obuf = buf;
#ifdef LIBVNCSERVER_HAVE_SASL
const char *output;
unsigned int outputlen;
int err;
#endif /* LIBVNCSERVER_HAVE_SASL */
if (client->serverPort==-1) if (client->serverPort==-1)
return TRUE; /* vncrec playing */ return TRUE; /* vncrec playing */
@ -259,9 +281,23 @@ WriteToRFBServer(rfbClient* client, char *buf, int n)
return TRUE; return TRUE;
} }
#ifdef LIBVNCSERVER_HAVE_SASL
if (client->saslconn) {
err = sasl_encode(client->saslconn,
buf, n,
&output, &outputlen);
if (err != SASL_OK) {
rfbClientLog("Failed to encode SASL data %s",
sasl_errstring(err, NULL, NULL));
return FALSE;
}
obuf = output;
n = outputlen;
}
#endif /* LIBVNCSERVER_HAVE_SASL */
while (i < n) { while (i < n) {
j = write(client->sock, buf + i, (n - i)); j = write(client->sock, obuf + i, (n - i));
if (j <= 0) { if (j <= 0) {
if (j < 0) { if (j < 0) {
#ifdef WIN32 #ifdef WIN32
@ -294,8 +330,6 @@ WriteToRFBServer(rfbClient* client, char *buf, int n)
return TRUE; return TRUE;
} }
static int initSockets() { static int initSockets() {
#ifdef WIN32 #ifdef WIN32
WSADATA trash; WSADATA trash;

@ -48,4 +48,9 @@ int WriteToTLS(rfbClient* client, char *buf, unsigned int n);
/* Free TLS resources */ /* Free TLS resources */
void FreeTLS(rfbClient* client); void FreeTLS(rfbClient* client);
#ifdef LIBVNCSERVER_HAVE_SASL
/* Get the number of bits in the current cipher */
int GetTLSCipherBits(rfbClient* client);
#endif /* LIBVNCSERVER_HAVE_SASL */
#endif /* TLS_H */ #endif /* TLS_H */

@ -170,7 +170,7 @@ InitializeTLSSession(rfbClient* client, rfbBool anonTLS)
static rfbBool static rfbBool
SetTLSAnonCredential(rfbClient* client) SetTLSAnonCredential(rfbClient* client)
{ {
gnutls_anon_client_credentials anonCred; gnutls_anon_client_credentials_t anonCred;
int ret; int ret;
if ((ret = gnutls_anon_allocate_client_credentials(&anonCred)) < 0 || if ((ret = gnutls_anon_allocate_client_credentials(&anonCred)) < 0 ||
@ -252,6 +252,10 @@ ReadVeNCryptSecurityType(rfbClient* client, uint32_t *result)
if (t==rfbVeNCryptTLSNone || if (t==rfbVeNCryptTLSNone ||
t==rfbVeNCryptTLSVNC || t==rfbVeNCryptTLSVNC ||
t==rfbVeNCryptTLSPlain || t==rfbVeNCryptTLSPlain ||
#ifdef LIBVNCSERVER_HAVE_SASL
t==rfbVeNCryptTLSSASL ||
t==rfbVeNCryptX509SASL ||
#endif /*LIBVNCSERVER_HAVE_SASL */
t==rfbVeNCryptX509None || t==rfbVeNCryptX509None ||
t==rfbVeNCryptX509VNC || t==rfbVeNCryptX509VNC ||
t==rfbVeNCryptX509Plain) t==rfbVeNCryptX509Plain)
@ -411,6 +415,9 @@ HandleVeNCryptAuth(rfbClient* client)
case rfbVeNCryptTLSNone: case rfbVeNCryptTLSNone:
case rfbVeNCryptTLSVNC: case rfbVeNCryptTLSVNC:
case rfbVeNCryptTLSPlain: case rfbVeNCryptTLSPlain:
#ifdef LIBVNCSERVER_HAVE_SASL
case rfbVeNCryptTLSSASL:
#endif /* LIBVNCSERVER_HAVE_SASL */
anonTLS = TRUE; anonTLS = TRUE;
break; break;
default: default:
@ -535,3 +542,14 @@ void FreeTLS(rfbClient* client)
client->tlsSession = NULL; client->tlsSession = NULL;
} }
} }
#ifdef LIBVNCSERVER_HAVE_SASL
int
GetTLSCipherBits(rfbClient* client)
{
gnutls_cipher_algorithm_t cipher = gnutls_cipher_get((gnutls_session_t)client->tlsSession);
return gnutls_cipher_get_key_size(cipher) * 8;
}
#endif /* LIBVNCSERVER_HAVE_SASL */

@ -56,3 +56,12 @@ void FreeTLS(rfbClient* client)
} }
#ifdef LIBVNCSERVER_HAVE_SASL
int
GetTLSCipherBits(rfbClient* client)
{
rfbClientLog("TLS is not supported.\n");
return 0;
}
#endif /* LIBVNCSERVER_HAVE_SASL */

@ -397,6 +397,10 @@ ReadVeNCryptSecurityType(rfbClient* client, uint32_t *result)
if (t==rfbVeNCryptTLSNone || if (t==rfbVeNCryptTLSNone ||
t==rfbVeNCryptTLSVNC || t==rfbVeNCryptTLSVNC ||
t==rfbVeNCryptTLSPlain || t==rfbVeNCryptTLSPlain ||
#ifdef LIBVNCSERVER_HAVE_SASL
t==rfbVeNCryptTLSSASL ||
t==rfbVeNCryptX509SASL ||
#endif /*LIBVNCSERVER_HAVE_SASL */
t==rfbVeNCryptX509None || t==rfbVeNCryptX509None ||
t==rfbVeNCryptX509VNC || t==rfbVeNCryptX509VNC ||
t==rfbVeNCryptX509Plain) t==rfbVeNCryptX509Plain)
@ -489,6 +493,9 @@ HandleVeNCryptAuth(rfbClient* client)
case rfbVeNCryptTLSNone: case rfbVeNCryptTLSNone:
case rfbVeNCryptTLSVNC: case rfbVeNCryptTLSVNC:
case rfbVeNCryptTLSPlain: case rfbVeNCryptTLSPlain:
#ifdef LIBVNCSERVER_HAVE_SASL
case rfbVeNCryptTLSSASL:
#endif /* LIBVNCSERVER_HAVE_SASL */
anonTLS = TRUE; anonTLS = TRUE;
break; break;
default: default:
@ -614,3 +621,14 @@ void FreeTLS(rfbClient* client)
SSL_free(client->tlsSession); SSL_free(client->tlsSession);
} }
#ifdef LIBVNCSERVER_HAVE_SASL
int GetTLSCipherBits(rfbClient* client)
{
SSL *ssl = (SSL *)(client->tlsSession);
const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
return SSL_CIPHER_get_bits(cipher, NULL);
}
#endif /* LIBVNCSERVER_HAVE_SASL */

@ -350,6 +350,13 @@ rfbClient* rfbGetClient(int bitsPerSample,int samplesPerPixel,
client->listen6Sock = -1; client->listen6Sock = -1;
client->listen6Address = NULL; client->listen6Address = NULL;
client->clientAuthSchemes = NULL; client->clientAuthSchemes = NULL;
#ifdef LIBVNCSERVER_HAVE_SASL
client->GetSASLMechanism = NULL;
client->GetUser = NULL;
client->saslSecret = NULL;
#endif /* LIBVNCSERVER_HAVE_SASL */
return client; return client;
} }
@ -534,5 +541,11 @@ void rfbClientCleanup(rfbClient* client) {
free(client->destHost); free(client->destHost);
if (client->clientAuthSchemes) if (client->clientAuthSchemes)
free(client->clientAuthSchemes); free(client->clientAuthSchemes);
#ifdef LIBVNCSERVER_HAVE_SASL
if (client->saslSecret)
free(client->saslSecret);
#endif /* LIBVNCSERVER_HAVE_SASL */
free(client); free(client);
} }

@ -52,6 +52,10 @@
#include <rfb/rfbproto.h> #include <rfb/rfbproto.h>
#include <rfb/keysym.h> #include <rfb/keysym.h>
#ifdef LIBVNCSERVER_HAVE_SASL
#include <sasl/sasl.h>
#endif /* LIBVNCSERVER_HAVE_SASL */
#define rfbClientSwap16IfLE(s) \ #define rfbClientSwap16IfLE(s) \
(*(char *)&client->endianTest ? ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) : (s)) (*(char *)&client->endianTest ? ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) : (s))
@ -197,6 +201,11 @@ typedef rfbBool (*GotJpegProc)(struct _rfbClient* client, const uint8_t* buffer,
typedef rfbBool (*LockWriteToTLSProc)(struct _rfbClient* client); typedef rfbBool (*LockWriteToTLSProc)(struct _rfbClient* client);
typedef rfbBool (*UnlockWriteToTLSProc)(struct _rfbClient* client); typedef rfbBool (*UnlockWriteToTLSProc)(struct _rfbClient* client);
#ifdef LIBVNCSERVER_HAVE_SASL
typedef char* (*GetUserProc)(struct _rfbClient* client);
typedef char* (*GetSASLMechanismProc)(struct _rfbClient* client, char* mechlist);
#endif /* LIBVNCSERVER_HAVE_SASL */
typedef struct _rfbClient { typedef struct _rfbClient {
uint8_t* frameBuffer; uint8_t* frameBuffer;
int width, height; int width, height;
@ -391,6 +400,20 @@ typedef struct _rfbClient {
GotBitmapProc GotBitmap; GotBitmapProc GotBitmap;
/** Hook for custom JPEG decoding and rendering */ /** Hook for custom JPEG decoding and rendering */
GotJpegProc GotJpeg; GotJpegProc GotJpeg;
#ifdef LIBVNCSERVER_HAVE_SASL
sasl_conn_t *saslconn;
const char *saslDecoded;
unsigned int saslDecodedLength;
unsigned int saslDecodedOffset;
sasl_secret_t *saslSecret;
/* Callback to allow the client to choose a preferred mechanism. The string returned will
be freed once no longer required. */
GetSASLMechanismProc GetSASLMechanism;
GetUserProc GetUser;
#endif /* LIBVNCSERVER_HAVE_SASL */
} rfbClient; } rfbClient;
/* cursor.c */ /* cursor.c */

@ -148,6 +148,9 @@
/* Define to 1 if OpenSSL is present */ /* Define to 1 if OpenSSL is present */
#cmakedefine LIBVNCSERVER_HAVE_LIBSSL 1 #cmakedefine LIBVNCSERVER_HAVE_LIBSSL 1
/* Define to 1 if Cyrus SASL is present */
#cmakedefine LIBVNCSERVER_HAVE_SASL 1
/* Define to 1 to build with websockets */ /* Define to 1 to build with websockets */
#cmakedefine LIBVNCSERVER_WITH_WEBSOCKETS 1 #cmakedefine LIBVNCSERVER_WITH_WEBSOCKETS 1

@ -67,9 +67,8 @@
typedef int8_t rfbBool; typedef int8_t rfbBool;
#include <sys/timeb.h> #include <sys/timeb.h>
#include <winsock2.h> #include <winsock2.h>
#else
#include <rfb/rfbconfig.h>
#endif #endif
#include <rfb/rfbconfig.h>
#ifdef LIBVNCSERVER_HAVE_LIBZ #ifdef LIBVNCSERVER_HAVE_LIBZ
#include <zlib.h> #include <zlib.h>
@ -287,6 +286,9 @@ typedef char rfbProtocolVersionMsg[13]; /* allow extra byte for null */
#define rfbUltra 17 #define rfbUltra 17
#define rfbTLS 18 #define rfbTLS 18
#define rfbVeNCrypt 19 #define rfbVeNCrypt 19
#ifdef LIBVNCSERVER_HAVE_SASL
#define rfbSASL 20
#endif /* LIBVNCSERVER_HAVE_SASL */
#define rfbARD 30 #define rfbARD 30
#define rfbMSLogon 0xfffffffa #define rfbMSLogon 0xfffffffa

Loading…
Cancel
Save