diff --git a/configure.ac b/configure.ac index e1950efd..701708d2 100644 --- a/configure.ac +++ b/configure.ac @@ -527,6 +527,33 @@ then fi +# +# TLS Locking Support within libVNCServer +# + +if test "x${have_libvncserver}" = "xyes" +then + + have_vnc_tls_locking=yes + AC_CHECK_MEMBERS([rfbClient.LockWriteToTLS, rfbClient.UnlockWriteToTLS], + [], [have_vnc_tls_locking=no], + [[#include ]]) + + if test "x${have_vnc_tls_locking}" = "xno" + then + AC_MSG_WARN([ + -------------------------------------------- + This version of libvncclient lacks support + for TLS locking. VNC connections that use + TLS may experience instability as documented + in GUACAMOLE-414]) + else + AC_DEFINE([ENABLE_VNC_TLS_LOCKING],, + [Whether support for TLS locking within VNC is enabled.]) + fi + +fi + # # FreeRDP # diff --git a/src/protocols/vnc/client.c b/src/protocols/vnc/client.c index 9cc85a18..cee1d4d5 100644 --- a/src/protocols/vnc/client.c +++ b/src/protocols/vnc/client.c @@ -36,6 +36,7 @@ #include +#include #include #include @@ -48,6 +49,11 @@ int guac_client_init(guac_client* client) { guac_vnc_client* vnc_client = calloc(1, sizeof(guac_vnc_client)); client->data = vnc_client; +#ifdef ENABLE_VNC_TLS_LOCKING + /* Initialize the write lock */ + pthread_mutex_init(&(vnc_client->tls_lock), NULL); +#endif + /* Init clipboard */ vnc_client->clipboard = guac_common_clipboard_alloc(GUAC_VNC_CLIPBOARD_MAX_LENGTH); @@ -125,6 +131,11 @@ int guac_vnc_client_free_handler(guac_client* client) { if (settings != NULL) guac_vnc_settings_free(settings); +#ifdef ENABLE_VNC_TLS_LOCKING + /* Clean up TLS lock mutex. */ + pthread_mutex_destroy(&(vnc_client->tls_lock)); +#endif + /* Free generic data struct */ free(client->data); diff --git a/src/protocols/vnc/vnc.c b/src/protocols/vnc/vnc.c index d9f9dbbb..a3ae9b12 100644 --- a/src/protocols/vnc/vnc.c +++ b/src/protocols/vnc/vnc.c @@ -55,6 +55,66 @@ char* GUAC_VNC_CLIENT_KEY = "GUAC_VNC"; +#ifdef ENABLE_VNC_TLS_LOCKING +/** + * A callback function that is called by the VNC library prior to writing + * data to a TLS-encrypted socket. This returns the rfbBool FALSE value + * if there's an error locking the mutex, or rfbBool TRUE otherwise. + * + * @param rfb_client + * The rfbClient for which to lock the TLS mutex. + * + * @returns + * rfbBool FALSE if an error occurs locking the mutex, otherwise + * TRUE. + */ +static rfbBool guac_vnc_lock_write_to_tls(rfbClient* rfb_client) { + + /* Retrieve the Guacamole data structures */ + guac_client* gc = rfbClientGetClientData(rfb_client, GUAC_VNC_CLIENT_KEY); + guac_vnc_client* vnc_client = (guac_vnc_client*) gc->data; + + /* Lock write access */ + int retval = pthread_mutex_lock(&(vnc_client->tls_lock)); + if (retval) { + guac_client_log(gc, GUAC_LOG_ERROR, "Error locking TLS write mutex: %s", + strerror(retval)); + return FALSE; + } + return TRUE; + +} + +/** + * A callback function for use by the VNC library that is called once + * the client is finished writing to a TLS-encrypted socket. A rfbBool + * FALSE value is returned if an error occurs unlocking the mutex, + * otherwise TRUE is returned. + * + * @param rfb_client + * The rfbClient for which to unlock the TLS mutex. + * + * @returns + * rfbBool FALSE if an error occurs unlocking the mutex, otherwise + * TRUE. + */ +static rfbBool guac_vnc_unlock_write_to_tls(rfbClient* rfb_client) { + + /* Retrieve the Guacamole data structures */ + guac_client* gc = rfbClientGetClientData(rfb_client, GUAC_VNC_CLIENT_KEY); + guac_vnc_client* vnc_client = (guac_vnc_client*) gc->data; + + /* Unlock write access */ + int retval = pthread_mutex_unlock(&(vnc_client->tls_lock)); + if (retval) { + guac_client_log(gc, GUAC_LOG_ERROR, "Error unlocking TLS write mutex: %s", + strerror(retval)); + return FALSE; + } + return TRUE; +} +#endif + rfbClient* guac_vnc_get_client(guac_client* client) { rfbClient* rfb_client = rfbGetClient(8, 3, 4); /* 32-bpp client */ @@ -68,6 +128,12 @@ rfbClient* guac_vnc_get_client(guac_client* client) { rfb_client->GotFrameBufferUpdate = guac_vnc_update; rfb_client->GotCopyRect = guac_vnc_copyrect; +#ifdef ENABLE_VNC_TLS_LOCKING + /* TLS Locking and Unlocking */ + rfb_client->LockWriteToTLS = guac_vnc_lock_write_to_tls; + rfb_client->UnlockWriteToTLS = guac_vnc_unlock_write_to_tls; +#endif + /* Do not handle clipboard and local cursor if read-only */ if (vnc_settings->read_only == 0) { @@ -403,4 +469,3 @@ void* guac_vnc_client_thread(void* data) { return NULL; } - diff --git a/src/protocols/vnc/vnc.h b/src/protocols/vnc/vnc.h index ce2d20af..7c189cfb 100644 --- a/src/protocols/vnc/vnc.h +++ b/src/protocols/vnc/vnc.h @@ -55,6 +55,13 @@ typedef struct guac_vnc_client { */ pthread_t client_thread; +#ifdef ENABLE_VNC_TLS_LOCKING + /** + * The TLS mutex lock for the client. + */ + pthread_mutex_t tls_lock; +#endif + /** * The underlying VNC client. */