diff --git a/include/rfb/rfb.h b/include/rfb/rfb.h index de083d7b..ecd82904 100644 --- a/include/rfb/rfb.h +++ b/include/rfb/rfb.h @@ -147,6 +147,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) */ @@ -155,6 +160,7 @@ typedef struct _rfbSecurity { uint8_t type; void (*handler)(struct _rfbClientRec* cl); struct _rfbSecurity* next; + enum rfbSecurityTag securityTags; } rfbSecurityHandler; /** @@ -402,6 +408,14 @@ typedef struct sraRegion* sraRegionPtr; typedef void (*ClientGoneHookPtr)(struct _rfbClientRec* cl); typedef void (*ClientFramebufferUpdateRequestHookPtr)(struct _rfbClientRec* cl, rfbFramebufferUpdateRequestMsg* furMsg); +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; @@ -496,7 +510,9 @@ typedef struct _rfbClientRec { * using LibVNCServer to provide services: */ RFB_INITIALISATION_SHARED, /**< sending initialisation messages with implicit shared-flag already true */ - RFB_SHUTDOWN /**< Client is shutting down */ + RFB_SHUTDOWN, /**< Client is shutting down */ + + RFB_CHANNEL_SECURITY_TYPE, /**< negotiating security (RFB v.3.7) */ } state; rfbBool reverseConnection; @@ -715,6 +731,11 @@ typedef struct _rfbClientRec { int destPort; /** ID on repeater in case of an UltraVNC mode 2 repeater connection */ char *repeaterId; + + ClientReadFromSocket readFromSocket; /* Read data from socket */ + ClientPeekAtSocket peekAtSocket; /* Peek at data from socket */ + ClientHasPendingOnSocket hasPendingOnSocket; /* Has pending data on socket */ + ClientWriteToSocket writeToSocket; /* Write data to socket */ } rfbClientRec, *rfbClientPtr; /** @@ -767,8 +788,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 rfbSocket rfbConnect(rfbScreenInfoPtr rfbScreen, char* host, int port); extern rfbSocket rfbConnectToTcpAddr(char* host, int port); @@ -874,6 +899,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 */ diff --git a/src/common/crypto.h b/src/common/crypto.h index 04be9304..c1f32194 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -11,7 +11,9 @@ int hash_md5(void *out, const void *in, const size_t in_len); /* Generates an SHA1 hash of 'in' and writes it to 'out', which must be 20 bytes in size. */ +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS int hash_sha1(void *out, const void *in, const size_t in_len); +#endif /* Fill 'out' with 'len' random bytes. */ void random_bytes(void *out, size_t len); diff --git a/src/common/crypto_included.c b/src/common/crypto_included.c index b359336f..cf8d43c2 100644 --- a/src/common/crypto_included.c +++ b/src/common/crypto_included.c @@ -33,6 +33,7 @@ int hash_md5(void *out, const void *in, const size_t in_len) return 0; } +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS int hash_sha1(void *out, const void *in, const size_t in_len) { SHA1Context sha1; @@ -45,6 +46,7 @@ int hash_sha1(void *out, const void *in, const size_t in_len) return 1; } +#endif /* LIBVNCSERVER_WITH_WEBSOCKETS */ void random_bytes(void *out, size_t len) { diff --git a/src/common/crypto_libgcrypt.c b/src/common/crypto_libgcrypt.c index 34d845b4..f62bdaf8 100644 --- a/src/common/crypto_libgcrypt.c +++ b/src/common/crypto_libgcrypt.c @@ -74,6 +74,7 @@ int hash_md5(void *out, const void *in, const size_t in_len) return result; } +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS int hash_sha1(void *out, const void *in, const size_t in_len) { int result = 0; @@ -98,6 +99,7 @@ int hash_sha1(void *out, const void *in, const size_t in_len) gcry_md_close(sha1); return result; } +#endif /* LIBVNCSERVER_WITH_WEBSOCKETS */ void random_bytes(void *out, size_t len) { diff --git a/src/common/crypto_openssl.c b/src/common/crypto_openssl.c index 28f04272..6bf9646c 100644 --- a/src/common/crypto_openssl.c +++ b/src/common/crypto_openssl.c @@ -52,6 +52,7 @@ int hash_md5(void *out, const void *in, const size_t in_len) return 1; } +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS int hash_sha1(void *out, const void *in, const size_t in_len) { SHA_CTX sha1; @@ -63,6 +64,7 @@ int hash_sha1(void *out, const void *in, const size_t in_len) return 0; return 1; } +#endif /* LIBVNCSERVER_WITH_WEBSOCKETS */ void random_bytes(void *out, size_t len) { diff --git a/src/libvncserver/auth.c b/src/libvncserver/auth.c index 748027b1..52ea2a36 100644 --- a/src/libvncserver/auth.c +++ b/src/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: rfbUnregisterSecurityHandler(&VncSecurityHandlerVncAuth); @@ -223,6 +275,9 @@ rfbSendSecurityTypeList(rfbClientPtr cl, int primaryType) for (handler = securityHandlers; handler && sizenext) { + if (exclude && (handler->securityTags & exclude)) + continue; + buffer[size] = handler->type; size++; } @@ -251,7 +306,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 && sizenext) { + 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; +} /* @@ -299,18 +376,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 handler required " + "security methods"); + return; + } if (securityType == rfbSecTypeInvalid) { rfbLog("VNC authentication disabled - RFB 3.3 client rejected\n"); rfbClientConnFailed(cl, "Your viewer cannot handle required " @@ -318,9 +396,13 @@ rfbAuthNewClient(rfbClientPtr cl) return; } rfbSendSecurityType(cl, securityType); + } else if (channelSecurityHandlers) { + rfbLog("Send channel security type list\n"); + rfbSendChannelSecurityTypeList(cl); } else { /* Here it's ok when securityType is set to rfbSecTypeInvalid. */ - rfbSendSecurityTypeList(cl, securityType); + rfbLog("Send channel security type 'none'\n"); + rfbSendSecurityTypeList(cl, RFB_SECURITY_TAG_NONE); } } @@ -334,6 +416,7 @@ rfbProcessClientSecurityType(rfbClientPtr cl) int n; uint8_t chosenType; rfbSecurityHandler* handler; + rfbSecurityHandler* handlerListHead; /* Read the security type. */ n = rfbReadExact(cl, (char *)&chosenType, 1); @@ -346,8 +429,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/src/libvncserver/rfbserver.c b/src/libvncserver/rfbserver.c index dd0dabb4..19233f66 100644 --- a/src/libvncserver/rfbserver.c +++ b/src/libvncserver/rfbserver.c @@ -376,6 +376,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; @@ -733,6 +737,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/src/libvncserver/sockets.c b/src/libvncserver/sockets.c index 6c87a2a1..3a4c6da7 100644 --- a/src/libvncserver/sockets.c +++ b/src/libvncserver/sockets.c @@ -101,6 +101,9 @@ int deny_severity=LOG_WARNING; int rfbMaxClientWait = 20000; /* time (ms) after which we decide client has gone away - needed to stop us hanging */ +static rfbBool +rfbHasPendingOnSocket(rfbClientPtr cl); + static rfbBool rfbNewConnectionFromSock(rfbScreenInfoPtr rfbScreen, rfbSocket sock) { @@ -364,16 +367,20 @@ rfbCheckFds(rfbScreenInfoPtr rfbScreen,long usec) tv.tv_usec = usec; nfds = select(rfbScreen->maxFd + 1, &fds, NULL, NULL /* &fds */, &tv); if (nfds == 0) { + rfbBool hasPendingData = FALSE; + /* timed out, check for async events */ i = rfbGetClientIterator(rfbScreen); while((cl = rfbClientIteratorNext(i))) { if (cl->onHold) continue; + hasPendingData |= rfbHasPendingOnSocket(cl); if (FD_ISSET(cl->sock, &(rfbScreen->allFds))) rfbSendFileTransferChunk(cl); } rfbReleaseClientIterator(i); - return result; + if (!hasPendingData) + return result; } if (nfds < 0) { @@ -449,9 +456,11 @@ rfbCheckFds(rfbScreenInfoPtr rfbScreen,long usec) if (cl->onHold) continue; - if (FD_ISSET(cl->sock, &(rfbScreen->allFds))) + if (rfbHasPendingOnSocket (cl) || + FD_ISSET(cl->sock, &(rfbScreen->allFds))) { - if (FD_ISSET(cl->sock, &fds)) + if (rfbHasPendingOnSocket (cl) || + FD_ISSET(cl->sock, &fds)) { #ifdef LIBVNCSERVER_WITH_WEBSOCKETS do { @@ -649,6 +658,30 @@ size_t fuzz_size; const uint8_t *fuzz_data; #endif +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) +{ + return 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 @@ -682,10 +715,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) { @@ -717,6 +750,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; @@ -753,6 +790,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) +{ + return 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 @@ -785,7 +834,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) { @@ -841,6 +890,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 @@ -905,7 +970,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) {