Jonas Ådahl 7 years ago
parent 46d9c246b9
commit 604fea4679

@ -0,0 +1,195 @@
From 0a98d629447964f1d5d922d5012ee0c2cbf10694 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Mon, 11 Jun 2018 23:47:02 +0200
Subject: [PATCH 1/2] libvncserver: Add API to add custom I/O entry points
Add API to make it possible to channel RFB input and output through
another layer, for example TLS. This is done by making it possible to
override the default read/write/peek functions.
---
libvncserver/rfbserver.c | 4 +++
libvncserver/sockets.c | 64 +++++++++++++++++++++++++++++++++++++---
rfb/rfb.h | 17 +++++++++++
3 files changed, 81 insertions(+), 4 deletions(-)
diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c
index 7af6aed..fbedd9f 100644
--- a/libvncserver/rfbserver.c
+++ b/libvncserver/rfbserver.c
@@ -322,6 +322,10 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen,
cl->screen = rfbScreen;
cl->sock = sock;
+ cl->readFromSocket = rfbDefaultReadFromSocket;
+ cl->peekAtSocket = rfbDefaultPeekAtSocket;
+ cl->hasPendingOnSocket = rfbDefaultHasPendingOnSocket;
+ cl->writeToSocket = rfbDefaultWriteToSocket;
cl->viewOnly = FALSE;
/* setup pseudo scaling */
cl->scaledScreen = rfbScreen;
diff --git a/libvncserver/sockets.c b/libvncserver/sockets.c
index bbc3d90..27515f2 100644
--- a/libvncserver/sockets.c
+++ b/libvncserver/sockets.c
@@ -589,6 +589,30 @@ rfbConnect(rfbScreenInfoPtr rfbScreen,
return sock;
}
+int
+rfbDefaultReadFromSocket(rfbClientPtr cl, char *buf, int len)
+{
+ return read(cl->sock, buf, len);
+}
+
+static int
+rfbReadFromSocket(rfbClientPtr cl, char *buf, int len)
+{
+ return cl->readFromSocket(cl, buf, len);
+}
+
+rfbBool
+rfbDefaultHasPendingOnSocket(rfbClientPtr cl)
+{
+ return FALSE;
+}
+
+static rfbBool
+rfbHasPendingOnSocket(rfbClientPtr cl)
+{
+ cl->hasPendingOnSocket(cl);
+}
+
/*
* ReadExact reads an exact number of bytes from a client. Returns 1 if
* those bytes have been read, 0 if the other end has closed, or -1 if an error
@@ -610,10 +634,10 @@ rfbReadExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout)
} else if (cl->sslctx) {
n = rfbssl_read(cl, buf, len);
} else {
- n = read(sock, buf, len);
+ n = rfbReadFromSocket(cl, buf, len);
}
#else
- n = read(sock, buf, len);
+ n = rfbReadFromSocket(cl, buf, len);
#endif
if (n > 0) {
@@ -645,6 +669,10 @@ rfbReadExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout)
continue;
}
#endif
+
+ if (rfbHasPendingOnSocket(cl))
+ continue;
+
FD_ZERO(&fds);
FD_SET(sock, &fds);
tv.tv_sec = timeout / 1000;
@@ -681,6 +709,18 @@ int rfbReadExact(rfbClientPtr cl,char* buf,int len)
return(rfbReadExactTimeout(cl,buf,len,rfbMaxClientWait));
}
+int
+rfbDefaultPeekAtSocket(rfbClientPtr cl, char *buf, int len)
+{
+ return recv(cl->sock, buf, len, MSG_PEEK);
+}
+
+int
+rfbPeekAtSocket(rfbClientPtr cl, char *buf, int len)
+{
+ cl->peekAtSocket(cl, buf, len);
+}
+
/*
* PeekExact peeks at an exact number of bytes from a client. Returns 1 if
* those bytes have been read, 0 if the other end has closed, or -1 if an
@@ -701,7 +741,7 @@ rfbPeekExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout)
n = rfbssl_peek(cl, buf, len);
else
#endif
- n = recv(sock, buf, len, MSG_PEEK);
+ n = rfbPeekAtSocket(cl, buf, len);
if (n == len) {
@@ -757,6 +797,22 @@ rfbPeekExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout)
return 1;
}
+int
+rfbDefaultWriteToSocket(rfbClientPtr cl,
+ const char *buf,
+ int len)
+{
+ return write(cl->sock, buf, len);
+}
+
+static int
+rfbWriteToSocket(rfbClientPtr cl,
+ const char *buf,
+ int len)
+{
+ return cl->writeToSocket(cl, buf, len);
+}
+
/*
* WriteExact writes an exact number of bytes to a client. Returns 1 if
* those bytes have been written, or -1 if an error occurred (errno is set to
@@ -801,7 +857,7 @@ rfbWriteExact(rfbClientPtr cl,
n = rfbssl_write(cl, buf, len);
else
#endif
- n = write(sock, buf, len);
+ n = rfbWriteToSocket(cl, buf, len);
if (n > 0) {
diff --git a/rfb/rfb.h b/rfb/rfb.h
index f982b40..ba9e898 100644
--- a/rfb/rfb.h
+++ b/rfb/rfb.h
@@ -415,6 +415,14 @@ typedef struct sraRegion* sraRegionPtr;
typedef void (*ClientGoneHookPtr)(struct _rfbClientRec* cl);
+typedef int (*ClientReadFromSocket)(struct _rfbClientRec* cl,
+ char *buf, int len);
+typedef int (*ClientPeekAtSocket)(struct _rfbClientRec* cl,
+ char *buf, int len);
+typedef rfbBool (*ClientHasPendingOnSocket)(struct _rfbClientRec* cl);
+typedef int (*ClientWriteToSocket)(struct _rfbClientRec* cl,
+ const char *buf, int len);
+
typedef struct _rfbFileTransferData {
int fd;
int compressionEnabled;
@@ -696,6 +704,11 @@ typedef struct _rfbClientRec {
wsCtx *wsctx;
char *wspath; /* Requests path component */
#endif
+
+ ClientReadFromSocket readFromSocket; /* Read data from socket */
+ ClientPeekAtSocket peekAtSocket; /* Peek at data from socket */
+ ClientHasPendingOnSocket hasPendingOnSocket; /* Peek at data from socket */
+ ClientWriteToSocket writeToSocket; /* Write data to socket */
} rfbClientRec, *rfbClientPtr;
/**
@@ -748,8 +761,12 @@ extern void rfbDisconnectUDPSock(rfbScreenInfoPtr rfbScreen);
extern void rfbCloseClient(rfbClientPtr cl);
extern int rfbReadExact(rfbClientPtr cl, char *buf, int len);
extern int rfbReadExactTimeout(rfbClientPtr cl, char *buf, int len,int timeout);
+extern int rfbDefaultReadFromSocket(rfbClientPtr cl, char *buf, int len);
extern int rfbPeekExactTimeout(rfbClientPtr cl, char *buf, int len,int timeout);
+extern int rfbDefaultPeekAtSocket(rfbClientPtr cl, char *buf, int len);
+extern rfbBool rfbDefaultHasPendingOnSocket(rfbClientPtr cl);
extern int rfbWriteExact(rfbClientPtr cl, const char *buf, int len);
+extern int rfbDefaultWriteToSocket(rfbClientPtr cl, const char *buf, int len);
extern int rfbCheckFds(rfbScreenInfoPtr rfbScreen,long usec);
extern int rfbConnect(rfbScreenInfoPtr rfbScreen, char* host, int port);
extern int rfbConnectToTcpAddr(char* host, int port);
--
2.17.1

@ -0,0 +1,366 @@
From c343c1b43080bcb45dad285faa5cd8926bfb9811 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Mon, 11 Jun 2018 23:50:05 +0200
Subject: [PATCH 2/2] libvncserver: Add channel security handlers
Add another type of security handler that is meant to be used initially
to set up a secure channel. Regular security handlers would be
advertised and processed after any channel security have succeeded.
For example, this, together with the custom I/O functions allows a
LibVNCServer user to implement TLS in combination with VNCAuth. This is
done by adding a single channel security handler with the rfbTLS (18)
with a handler that initiates a TLS session, and when a TLS session is
initiated, the regular security handler list is sent.
---
libvncserver/auth.c | 162 ++++++++++++++++++++++++++++++---------
libvncserver/rfbserver.c | 1 +
rfb/rfb.h | 15 +++-
3 files changed, 140 insertions(+), 38 deletions(-)
diff --git a/libvncserver/auth.c b/libvncserver/auth.c
index 814a814..6581953 100644
--- a/libvncserver/auth.c
+++ b/libvncserver/auth.c
@@ -37,18 +37,17 @@ void rfbClientSendString(rfbClientPtr cl, const char *reason);
* Handle security types
*/
+/* Channel security handlers to set up a secure channel, e.g. TLS. */
+static rfbSecurityHandler* channelSecurityHandlers = NULL;
+
+/* Security handlers when channel security is established. */
static rfbSecurityHandler* securityHandlers = NULL;
-/*
- * This method registers a list of new security types.
- * It avoids same security type getting registered multiple times.
- * The order is not preserved if multiple security types are
- * registered at one-go.
- */
void
-rfbRegisterSecurityHandler(rfbSecurityHandler* handler)
+rfbRegisterSecurityHandlerTo(rfbSecurityHandler* handler,
+ rfbSecurityHandler** handlerList)
{
- rfbSecurityHandler *head = securityHandlers, *next = NULL;
+ rfbSecurityHandler *head = *handlerList, *next = NULL;
if(handler == NULL)
return;
@@ -57,39 +56,35 @@ rfbRegisterSecurityHandler(rfbSecurityHandler* handler)
while(head != NULL) {
if(head == handler) {
- rfbRegisterSecurityHandler(next);
+ rfbRegisterSecurityHandlerTo(next, handlerList);
return;
}
head = head->next;
}
- handler->next = securityHandlers;
- securityHandlers = handler;
+ handler->next = *handlerList;
+ *handlerList = handler;
- rfbRegisterSecurityHandler(next);
+ rfbRegisterSecurityHandlerTo(next, handlerList);
}
-/*
- * This method unregisters a list of security types.
- * These security types won't be available for any new
- * client connection.
- */
-void
-rfbUnregisterSecurityHandler(rfbSecurityHandler* handler)
+static void
+rfbUnregisterSecurityHandlerFrom(rfbSecurityHandler* handler,
+ rfbSecurityHandler** handlerList)
{
rfbSecurityHandler *cur = NULL, *pre = NULL;
if(handler == NULL)
return;
- if(securityHandlers == handler) {
- securityHandlers = securityHandlers->next;
- rfbUnregisterSecurityHandler(handler->next);
+ if(*handlerList == handler) {
+ *handlerList = (*handlerList)->next;
+ rfbUnregisterSecurityHandlerFrom(handler->next, handlerList);
return;
}
- cur = pre = securityHandlers;
+ cur = pre = *handlerList;
while(cur) {
if(cur == handler) {
@@ -99,7 +94,50 @@ rfbUnregisterSecurityHandler(rfbSecurityHandler* handler)
pre = cur;
cur = cur->next;
}
- rfbUnregisterSecurityHandler(handler->next);
+ rfbUnregisterSecurityHandlerFrom(handler->next, handlerList);
+}
+
+void
+rfbRegisterChannelSecurityHandler(rfbSecurityHandler* handler)
+{
+ rfbRegisterSecurityHandlerTo(handler, &channelSecurityHandlers);
+}
+
+/*
+ * This method unregisters a list of security types.
+ * These security types won't be available for any new
+ * client connection.
+ */
+
+void
+rfbUnregisterChannelSecurityHandler(rfbSecurityHandler* handler)
+{
+ rfbUnregisterSecurityHandlerFrom(handler, &channelSecurityHandlers);
+}
+
+/*
+ * This method registers a list of new security types.
+ * It avoids same security type getting registered multiple times.
+ * The order is not preserved if multiple security types are
+ * registered at one-go.
+ */
+
+void
+rfbRegisterSecurityHandler(rfbSecurityHandler* handler)
+{
+ rfbRegisterSecurityHandlerTo(handler, &securityHandlers);
+}
+
+/*
+ * This method unregisters a list of security types.
+ * These security types won't be available for any new
+ * client connection.
+ */
+
+void
+rfbUnregisterSecurityHandler(rfbSecurityHandler* handler)
+{
+ rfbUnregisterSecurityHandlerFrom(handler, &securityHandlers);
}
/*
@@ -197,9 +235,22 @@ static rfbSecurityHandler VncSecurityHandlerNone = {
NULL
};
+static int32_t
+determinePrimarySecurityType(rfbClientPtr cl)
+{
+ if (!cl->screen->authPasswdData || cl->reverseConnection) {
+ /* chk if this condition is valid or not. */
+ return rfbSecTypeNone;
+ } else if (cl->screen->authPasswdData) {
+ return rfbSecTypeVncAuth;
+ } else {
+ return rfbSecTypeInvalid;
+ }
+}
-static void
-rfbSendSecurityTypeList(rfbClientPtr cl, int primaryType)
+void
+rfbSendSecurityTypeList(rfbClientPtr cl,
+ enum rfbSecurityTag exclude)
{
/* The size of the message is the count of security types +1,
* since the first byte is the number of types. */
@@ -207,9 +258,10 @@ rfbSendSecurityTypeList(rfbClientPtr cl, int primaryType)
rfbSecurityHandler* handler;
#define MAX_SECURITY_TYPES 255
uint8_t buffer[MAX_SECURITY_TYPES+1];
-
+ int32_t primaryType;
/* Fill in the list of security types in the client structure. (NOTE: Not really in the client structure) */
+ primaryType = determinePrimarySecurityType(cl);
switch (primaryType) {
case rfbSecTypeNone:
rfbRegisterSecurityHandler(&VncSecurityHandlerNone);
@@ -221,6 +273,9 @@ rfbSendSecurityTypeList(rfbClientPtr cl, int primaryType)
for (handler = securityHandlers;
handler && size<MAX_SECURITY_TYPES; handler = handler->next) {
+ if (exclude && (handler->securityTags & exclude))
+ continue;
+
buffer[size] = handler->type;
size++;
}
@@ -249,7 +304,29 @@ rfbSendSecurityTypeList(rfbClientPtr cl, int primaryType)
cl->state = RFB_SECURITY_TYPE;
}
+static void
+rfbSendChannelSecurityTypeList(rfbClientPtr cl)
+{
+ int size = 1;
+ rfbSecurityHandler* handler;
+ uint8_t buffer[MAX_SECURITY_TYPES+1];
+
+ for (handler = channelSecurityHandlers;
+ handler && size<MAX_SECURITY_TYPES; handler = handler->next) {
+ buffer[size] = handler->type;
+ size++;
+ }
+ buffer[0] = (unsigned char)size-1;
+
+ if (rfbWriteExact(cl, (char *)buffer, size) < 0) {
+ rfbLogPerror("rfbSendSecurityTypeList: write");
+ rfbCloseClient(cl);
+ return;
+ }
+ /* Dispatch client input to rfbProcessClientChannelSecurityType. */
+ cl->state = RFB_CHANNEL_SECURITY_TYPE;
+}
/*
@@ -297,18 +374,19 @@ rfbSendSecurityType(rfbClientPtr cl, int32_t securityType)
void
rfbAuthNewClient(rfbClientPtr cl)
{
- int32_t securityType = rfbSecTypeInvalid;
+ int32_t securityType;
- if (!cl->screen->authPasswdData || cl->reverseConnection) {
- /* chk if this condition is valid or not. */
- securityType = rfbSecTypeNone;
- } else if (cl->screen->authPasswdData) {
- securityType = rfbSecTypeVncAuth;
- }
+ securityType = determinePrimarySecurityType(cl);
if (cl->protocolMajorVersion==3 && cl->protocolMinorVersion < 7)
{
/* Make sure we use only RFB 3.3 compatible security types. */
+ if (channelSecurityHandlers) {
+ rfbLog("VNC channel security enabled - RFB 3.3 client rejected\n");
+ rfbClientConnFailed(cl, "Your viewer cannot hnadler required "
+ "security methods");
+ return;
+ }
if (securityType == rfbSecTypeInvalid) {
rfbLog("VNC authentication disabled - RFB 3.3 client rejected\n");
rfbClientConnFailed(cl, "Your viewer cannot handle required "
@@ -316,9 +394,11 @@ rfbAuthNewClient(rfbClientPtr cl)
return;
}
rfbSendSecurityType(cl, securityType);
+ } else if (channelSecurityHandlers) {
+ rfbSendChannelSecurityTypeList(cl);
} else {
/* Here it's ok when securityType is set to rfbSecTypeInvalid. */
- rfbSendSecurityTypeList(cl, securityType);
+ rfbSendSecurityTypeList(cl, RFB_SECURITY_TAG_NONE);
}
}
@@ -332,6 +412,7 @@ rfbProcessClientSecurityType(rfbClientPtr cl)
int n;
uint8_t chosenType;
rfbSecurityHandler* handler;
+ rfbSecurityHandler* handlerListHead;
/* Read the security type. */
n = rfbReadExact(cl, (char *)&chosenType, 1);
@@ -344,8 +425,17 @@ rfbProcessClientSecurityType(rfbClientPtr cl)
return;
}
+ switch (cl->state) {
+ case RFB_CHANNEL_SECURITY_TYPE:
+ handlerListHead = channelSecurityHandlers;
+ break;
+ case RFB_SECURITY_TYPE:
+ handlerListHead = securityHandlers;
+ break;
+ }
+
/* Make sure it was present in the list sent by the server. */
- for (handler = securityHandlers; handler; handler = handler->next) {
+ for (handler = handlerListHead; handler; handler = handler->next) {
if (chosenType == handler->type) {
rfbLog("rfbProcessClientSecurityType: executing handler for type %d\n", chosenType);
handler->handler(cl);
diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c
index fbedd9f..1e8b3c1 100644
--- a/libvncserver/rfbserver.c
+++ b/libvncserver/rfbserver.c
@@ -643,6 +643,7 @@ rfbProcessClientMessage(rfbClientPtr cl)
case RFB_PROTOCOL_VERSION:
rfbProcessClientProtocolVersion(cl);
return;
+ case RFB_CHANNEL_SECURITY_TYPE:
case RFB_SECURITY_TYPE:
rfbProcessClientSecurityType(cl);
return;
diff --git a/rfb/rfb.h b/rfb/rfb.h
index ba9e898..be58d08 100644
--- a/rfb/rfb.h
+++ b/rfb/rfb.h
@@ -182,6 +182,11 @@ typedef struct {
} data; /**< there have to be count*3 entries */
} rfbColourMap;
+enum rfbSecurityTag {
+ RFB_SECURITY_TAG_NONE = 0,
+ RFB_SECURITY_TAG_CHANNEL = 1 << 0
+};
+
/**
* Security handling (RFB protocol version 3.7)
*/
@@ -190,6 +195,7 @@ typedef struct _rfbSecurity {
uint8_t type;
void (*handler)(struct _rfbClientRec* cl);
struct _rfbSecurity* next;
+ enum rfbSecurityTag securityTags;
} rfbSecurityHandler;
/**
@@ -506,7 +512,7 @@ typedef struct _rfbClientRec {
/** Possible client states: */
enum {
RFB_PROTOCOL_VERSION, /**< establishing protocol version */
- RFB_SECURITY_TYPE, /**< negotiating security (RFB v.3.7) */
+ RFB_SECURITY_TYPE, /**< negotiating security (RFB v.3.7) */
RFB_AUTHENTICATION, /**< authenticating */
RFB_INITIALISATION, /**< sending initialisation messages */
RFB_NORMAL, /**< normal protocol messages */
@@ -514,7 +520,9 @@ typedef struct _rfbClientRec {
/* Ephemeral internal-use states that will never be seen by software
* using LibVNCServer to provide services: */
- RFB_INITIALISATION_SHARED /**< sending initialisation messages with implicit shared-flag already true */
+ RFB_INITIALISATION_SHARED, /**< sending initialisation messages with implicit shared-flag already true */
+
+ RFB_CHANNEL_SECURITY_TYPE, /**< negotiating security (RFB v.3.7) */
} state;
rfbBool reverseConnection;
@@ -855,6 +863,9 @@ extern void rfbProcessClientSecurityType(rfbClientPtr cl);
extern void rfbAuthProcessClientMessage(rfbClientPtr cl);
extern void rfbRegisterSecurityHandler(rfbSecurityHandler* handler);
extern void rfbUnregisterSecurityHandler(rfbSecurityHandler* handler);
+extern void rfbRegisterChannelSecurityHandler(rfbSecurityHandler* handler);
+extern void rfbUnregisterChannelSecurityHandler(rfbSecurityHandler* handler);
+extern void rfbSendSecurityTypeList(rfbClientPtr cl, enum rfbSecurityTag exclude);
/* rre.c */
--
2.17.1

@ -1,7 +1,7 @@
Summary: Library to make writing a VNC server easy
Name: libvncserver
Version: 0.9.11
Release: 6%{?dist}
Release: 7%{?dist}
# NOTE: --with-filetransfer => GPLv2
License: GPLv2+
@ -11,6 +11,11 @@ Source0: https://github.com/LibVNC/libvncserver/archive/LibVNCServer-%{versio
## upstream patches
Patch4: 0040-Ensure-compatibility-with-gtk-vnc-0.7.0.patch
## TLS security type enablement patches
# https://github.com/LibVNC/libvncserver/pull/234
Patch10: 0001-libvncserver-Add-API-to-add-custom-I-O-entry-points.patch
Patch11: 0002-libvncserver-Add-channel-security-handlers.patch
## downstream patches
Patch100: libvncserver-0.9.11-system_minilzo.patch
Patch101: libvncserver-0.9.1-multilib.patch
@ -72,6 +77,9 @@ developing applications that use %{name}.
%patch4 -p1 -b .0004
%patch10 -p1
%patch11 -p1
%patch100 -p1 -b .system_minilzo
# Nuke bundled minilzo
#rm -fv common/lzodefs.h common/lzoconf.h commmon/minilzo.h common/minilzo.c
@ -145,6 +153,9 @@ make -C test test ||:
%changelog
* Tue Jun 19 2018 Jonas Ådahl <jadahl@redhat.com> - 0.9.11-7
- Add API to enable implementing TLS security type
* Mon Feb 26 2018 Petr Pisar <ppisar@redhat.com> - 0.9.11-6
- Fix CVE-2018-7225 (bug #1546860)

Loading…
Cancel
Save