aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/core/CMakeLists.txt4
-rw-r--r--common/core/Configuration.cxx4
-rw-r--r--common/core/Configuration.h2
-rw-r--r--common/core/Mutex.cxx157
-rw-r--r--common/core/Mutex.h64
-rw-r--r--common/core/Thread.cxx172
-rw-r--r--common/core/Thread.h58
-rw-r--r--common/core/string.cxx3
-rw-r--r--common/network/Socket.cxx2
-rw-r--r--common/network/TcpSocket.cxx2
-rw-r--r--common/rdr/CMakeLists.txt2
-rw-r--r--common/rdr/InStream.h8
-rw-r--r--common/rdr/TLSException.cxx23
-rw-r--r--common/rdr/TLSException.h6
-rw-r--r--common/rdr/TLSInStream.cxx91
-rw-r--r--common/rdr/TLSInStream.h18
-rw-r--r--common/rdr/TLSOutStream.cxx77
-rw-r--r--common/rdr/TLSOutStream.h17
-rw-r--r--common/rdr/TLSSocket.cxx228
-rw-r--r--common/rdr/TLSSocket.h81
-rw-r--r--common/rfb/CConnection.cxx138
-rw-r--r--common/rfb/CConnection.h191
-rw-r--r--common/rfb/CMakeLists.txt16
-rw-r--r--common/rfb/CMsgHandler.cxx168
-rw-r--r--common/rfb/CMsgHandler.h42
-rw-r--r--common/rfb/CSecurityTLS.cxx210
-rw-r--r--common/rfb/CSecurityTLS.h7
-rw-r--r--common/rfb/ClientParams.cxx13
-rw-r--r--common/rfb/DecodeManager.cxx75
-rw-r--r--common/rfb/DecodeManager.h18
-rw-r--r--common/rfb/EncodeManager.cxx7
-rw-r--r--common/rfb/EncodeManager.h2
-rw-r--r--common/rfb/SConnection.cxx103
-rw-r--r--common/rfb/SConnection.h142
-rw-r--r--common/rfb/SMsgHandler.cxx176
-rw-r--r--common/rfb/SMsgHandler.h73
-rw-r--r--common/rfb/SSecurityPlain.cxx5
-rw-r--r--common/rfb/SSecurityPlain.h12
-rw-r--r--common/rfb/SSecurityRSAAES.cxx5
-rw-r--r--common/rfb/SSecurityTLS.cxx102
-rw-r--r--common/rfb/SSecurityTLS.h7
-rw-r--r--common/rfb/ServerParams.cxx11
-rw-r--r--common/rfb/UnixPasswordValidator.cxx107
-rw-r--r--common/rfb/UnixPasswordValidator.h14
-rw-r--r--common/rfb/VNCSConnectionST.cxx18
-rw-r--r--common/rfb/VNCServerST.cxx29
-rw-r--r--common/rfb/WinPasswdValidator.cxx3
-rw-r--r--common/rfb/WinPasswdValidator.h6
-rw-r--r--common/rfb/pam.c95
-rw-r--r--common/rfb/pam.h34
50 files changed, 1194 insertions, 1654 deletions
diff --git a/common/core/CMakeLists.txt b/common/core/CMakeLists.txt
index 1932e5db..7e58acc0 100644
--- a/common/core/CMakeLists.txt
+++ b/common/core/CMakeLists.txt
@@ -5,10 +5,8 @@ add_library(core STATIC
Logger_file.cxx
Logger_stdio.cxx
LogWriter.cxx
- Mutex.cxx
Region.cxx
Timer.cxx
- Thread.cxx
string.cxx
time.cxx
xdgdirs.cxx)
@@ -16,11 +14,9 @@ add_library(core STATIC
target_include_directories(core PUBLIC ${CMAKE_SOURCE_DIR}/common)
target_include_directories(core SYSTEM PUBLIC ${PIXMAN_INCLUDE_DIRS})
target_link_libraries(core ${PIXMAN_LIBRARIES})
-target_link_directories(core PUBLIC ${PIXMAN_LIBRARY_DIRS})
if(UNIX)
target_sources(core PRIVATE Logger_syslog.cxx)
- target_link_libraries(core pthread)
endif()
if(WIN32)
diff --git a/common/core/Configuration.cxx b/common/core/Configuration.cxx
index 129d1b9e..e6affb06 100644
--- a/common/core/Configuration.cxx
+++ b/common/core/Configuration.cxx
@@ -570,6 +570,10 @@ bool ListParameter<ValueType>::setParam(const char* v)
entry.erase(0, entry.find_first_not_of(" \f\n\r\t\v"));
entry.erase(entry.find_last_not_of(" \f\n\r\t\v")+1);
+ // Special case, entire v was just whitespace
+ if (entry.empty() && (entries.size() == 1))
+ break;
+
if (!decodeEntry(entry.c_str(), &e)) {
vlog.error("List parameter %s: Invalid value '%s'",
getName(), entry.c_str());
diff --git a/common/core/Configuration.h b/common/core/Configuration.h
index 57bc48e1..431dd0c5 100644
--- a/common/core/Configuration.h
+++ b/common/core/Configuration.h
@@ -86,6 +86,8 @@ namespace core {
std::list<VoidParameter*>::iterator begin() { return params.begin(); }
std::list<VoidParameter*>::iterator end() { return params.end(); }
+ // - Returns the number of parameters
+ int size() { return params.size(); }
// - Get the Global Configuration object
// NB: This call does NOT lock the Configuration system.
diff --git a/common/core/Mutex.cxx b/common/core/Mutex.cxx
deleted file mode 100644
index 6f72581e..00000000
--- a/common/core/Mutex.cxx
+++ /dev/null
@@ -1,157 +0,0 @@
-/* Copyright 2015 Pierre Ossman for Cendio AB
- *
- * 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.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#ifdef WIN32
-#include <windows.h>
-#else
-#include <pthread.h>
-#endif
-
-#include <core/Exception.h>
-#include <core/Mutex.h>
-
-using namespace core;
-
-Mutex::Mutex()
-{
-#ifdef WIN32
- systemMutex = new CRITICAL_SECTION;
- InitializeCriticalSection((CRITICAL_SECTION*)systemMutex);
-#else
- int ret;
-
- systemMutex = new pthread_mutex_t;
- ret = pthread_mutex_init((pthread_mutex_t*)systemMutex, nullptr);
- if (ret != 0)
- throw posix_error("Failed to create mutex", ret);
-#endif
-}
-
-Mutex::~Mutex()
-{
-#ifdef WIN32
- DeleteCriticalSection((CRITICAL_SECTION*)systemMutex);
- delete (CRITICAL_SECTION*)systemMutex;
-#else
- pthread_mutex_destroy((pthread_mutex_t*)systemMutex);
- delete (pthread_mutex_t*)systemMutex;
-#endif
-}
-
-void Mutex::lock()
-{
-#ifdef WIN32
- EnterCriticalSection((CRITICAL_SECTION*)systemMutex);
-#else
- int ret;
-
- ret = pthread_mutex_lock((pthread_mutex_t*)systemMutex);
- if (ret != 0)
- throw posix_error("Failed to lock mutex", ret);
-#endif
-}
-
-void Mutex::unlock()
-{
-#ifdef WIN32
- LeaveCriticalSection((CRITICAL_SECTION*)systemMutex);
-#else
- int ret;
-
- ret = pthread_mutex_unlock((pthread_mutex_t*)systemMutex);
- if (ret != 0)
- throw posix_error("Failed to unlock mutex", ret);
-#endif
-}
-
-Condition::Condition(Mutex* mutex_)
-{
- this->mutex = mutex_;
-
-#ifdef WIN32
- systemCondition = new CONDITION_VARIABLE;
- InitializeConditionVariable((CONDITION_VARIABLE*)systemCondition);
-#else
- int ret;
-
- systemCondition = new pthread_cond_t;
- ret = pthread_cond_init((pthread_cond_t*)systemCondition, nullptr);
- if (ret != 0)
- throw posix_error("Failed to create condition variable", ret);
-#endif
-}
-
-Condition::~Condition()
-{
-#ifdef WIN32
- delete (CONDITION_VARIABLE*)systemCondition;
-#else
- pthread_cond_destroy((pthread_cond_t*)systemCondition);
- delete (pthread_cond_t*)systemCondition;
-#endif
-}
-
-void Condition::wait()
-{
-#ifdef WIN32
- BOOL ret;
-
- ret = SleepConditionVariableCS((CONDITION_VARIABLE*)systemCondition,
- (CRITICAL_SECTION*)mutex->systemMutex,
- INFINITE);
- if (!ret)
- throw win32_error("Failed to wait on condition variable", GetLastError());
-#else
- int ret;
-
- ret = pthread_cond_wait((pthread_cond_t*)systemCondition,
- (pthread_mutex_t*)mutex->systemMutex);
- if (ret != 0)
- throw posix_error("Failed to wait on condition variable", ret);
-#endif
-}
-
-void Condition::signal()
-{
-#ifdef WIN32
- WakeConditionVariable((CONDITION_VARIABLE*)systemCondition);
-#else
- int ret;
-
- ret = pthread_cond_signal((pthread_cond_t*)systemCondition);
- if (ret != 0)
- throw posix_error("Failed to signal condition variable", ret);
-#endif
-}
-
-void Condition::broadcast()
-{
-#ifdef WIN32
- WakeAllConditionVariable((CONDITION_VARIABLE*)systemCondition);
-#else
- int ret;
-
- ret = pthread_cond_broadcast((pthread_cond_t*)systemCondition);
- if (ret != 0)
- throw posix_error("Failed to broadcast condition variable", ret);
-#endif
-}
diff --git a/common/core/Mutex.h b/common/core/Mutex.h
deleted file mode 100644
index 17a97d1b..00000000
--- a/common/core/Mutex.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* Copyright 2015 Pierre Ossman for Cendio AB
- *
- * 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.
- */
-
-#ifndef __CORE_MUTEX_H__
-#define __CORE_MUTEX_H__
-
-namespace core {
- class Condition;
-
- class Mutex {
- public:
- Mutex();
- ~Mutex();
-
- void lock();
- void unlock();
-
- private:
- friend class Condition;
-
- void* systemMutex;
- };
-
- class AutoMutex {
- public:
- AutoMutex(Mutex* mutex) { m = mutex; m->lock(); }
- ~AutoMutex() { m->unlock(); }
- private:
- Mutex* m;
- };
-
- class Condition {
- public:
- Condition(Mutex* mutex);
- ~Condition();
-
- void wait();
-
- void signal();
- void broadcast();
-
- private:
- Mutex* mutex;
- void* systemCondition;
- };
-
-}
-
-#endif
diff --git a/common/core/Thread.cxx b/common/core/Thread.cxx
deleted file mode 100644
index be518cd4..00000000
--- a/common/core/Thread.cxx
+++ /dev/null
@@ -1,172 +0,0 @@
-/* Copyright 2015 Pierre Ossman for Cendio AB
- *
- * 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.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#ifdef WIN32
-#include <windows.h>
-#else
-#include <pthread.h>
-#include <signal.h>
-#include <unistd.h>
-#endif
-
-#include <core/Exception.h>
-#include <core/Mutex.h>
-#include <core/Thread.h>
-
-using namespace core;
-
-Thread::Thread() : running(false), threadId(nullptr)
-{
- mutex = new Mutex;
-
-#ifdef WIN32
- threadId = new HANDLE;
-#else
- threadId = new pthread_t;
-#endif
-}
-
-Thread::~Thread()
-{
-#ifdef WIN32
- delete (HANDLE*)threadId;
-#else
- if (isRunning())
- pthread_cancel(*(pthread_t*)threadId);
- delete (pthread_t*)threadId;
-#endif
-
- delete mutex;
-}
-
-void Thread::start()
-{
- AutoMutex a(mutex);
-
-#ifdef WIN32
- *(HANDLE*)threadId = CreateThread(nullptr, 0, startRoutine, this, 0, nullptr);
- if (*(HANDLE*)threadId == nullptr)
- throw win32_error("Failed to create thread", GetLastError());
-#else
- int ret;
- sigset_t all, old;
-
- // Creating threads from libraries is a bit evil, so mitigate the
- // issue by at least avoiding signals on these threads
- sigfillset(&all);
- ret = pthread_sigmask(SIG_SETMASK, &all, &old);
- if (ret != 0)
- throw posix_error("Failed to mask signals", ret);
-
- ret = pthread_create((pthread_t*)threadId, nullptr, startRoutine, this);
-
- pthread_sigmask(SIG_SETMASK, &old, nullptr);
-
- if (ret != 0)
- throw posix_error("Failed to create thread", ret);
-#endif
-
- running = true;
-}
-
-void Thread::wait()
-{
- if (!isRunning())
- return;
-
-#ifdef WIN32
- DWORD ret;
-
- ret = WaitForSingleObject(*(HANDLE*)threadId, INFINITE);
- if (ret != WAIT_OBJECT_0)
- throw win32_error("Failed to join thread", GetLastError());
-#else
- int ret;
-
- ret = pthread_join(*(pthread_t*)threadId, nullptr);
- if (ret != 0)
- throw posix_error("Failed to join thread", ret);
-#endif
-}
-
-bool Thread::isRunning()
-{
- AutoMutex a(mutex);
-
- return running;
-}
-
-size_t Thread::getSystemCPUCount()
-{
-#ifdef WIN32
- SYSTEM_INFO si;
- size_t count;
- DWORD mask;
-
- GetSystemInfo(&si);
-
- count = 0;
- for (mask = si.dwActiveProcessorMask;mask != 0;mask >>= 1) {
- if (mask & 0x1)
- count++;
- }
-
- if (count > si.dwNumberOfProcessors)
- count = si.dwNumberOfProcessors;
-
- return count;
-#else
- long ret;
-
- ret = sysconf(_SC_NPROCESSORS_ONLN);
- if (ret == -1)
- return 0;
-
- return ret;
-#endif
-}
-
-#ifdef WIN32
-long unsigned __stdcall Thread::startRoutine(void* data)
-#else
-void* Thread::startRoutine(void* data)
-#endif
-{
- Thread *self;
-
- self = (Thread*)data;
-
- try {
- self->worker();
- } catch(...) {
- }
-
- self->mutex->lock();
- self->running = false;
- self->mutex->unlock();
-
-#ifdef WIN32
- return 0;
-#else
- return nullptr;
-#endif
-}
diff --git a/common/core/Thread.h b/common/core/Thread.h
deleted file mode 100644
index 53c5ef18..00000000
--- a/common/core/Thread.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Copyright 2015 Pierre Ossman for Cendio AB
- *
- * 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.
- */
-
-#ifndef __CORE_THREAD_H__
-#define __CORE_THREAD_H__
-
-#include <stddef.h>
-
-namespace core {
- class Mutex;
-
- class Thread {
- public:
- Thread();
- virtual ~Thread();
-
- void start();
- void wait();
-
- bool isRunning();
-
- public:
- static size_t getSystemCPUCount();
-
- protected:
- virtual void worker() = 0;
-
- private:
-#ifdef WIN32
- static long unsigned __stdcall startRoutine(void* data);
-#else
- static void* startRoutine(void* data);
-#endif
-
- private:
- Mutex *mutex;
- bool running;
-
- void *threadId;
- };
-}
-
-#endif
diff --git a/common/core/string.cxx b/common/core/string.cxx
index 49501a9f..091836db 100644
--- a/common/core/string.cxx
+++ b/common/core/string.cxx
@@ -64,6 +64,9 @@ namespace core {
std::vector<std::string> out;
const char *start, *stop;
+ if (src[0] == '\0')
+ return out;
+
start = src;
do {
stop = strchr(start, delimiter);
diff --git a/common/network/Socket.cxx b/common/network/Socket.cxx
index f5b44239..7fc39d1e 100644
--- a/common/network/Socket.cxx
+++ b/common/network/Socket.cxx
@@ -120,7 +120,7 @@ void Socket::shutdown()
}
isShutdown_ = true;
- ::shutdown(getFd(), SHUT_RDWR);
+ ::shutdown(getFd(), SHUT_WR);
}
bool Socket::isShutdown() const
diff --git a/common/network/TcpSocket.cxx b/common/network/TcpSocket.cxx
index e941aa67..bf3a224c 100644
--- a/common/network/TcpSocket.cxx
+++ b/common/network/TcpSocket.cxx
@@ -698,7 +698,7 @@ TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
if (parts.size() > 2)
throw std::invalid_argument("Invalid filter specified");
- if (parts[0].empty()) {
+ if (parts.empty() || parts[0].empty()) {
// Match any address
memset (&pattern.address, 0, sizeof (pattern.address));
pattern.address.u.sa.sa_family = AF_UNSPEC;
diff --git a/common/rdr/CMakeLists.txt b/common/rdr/CMakeLists.txt
index 55c69132..526b2971 100644
--- a/common/rdr/CMakeLists.txt
+++ b/common/rdr/CMakeLists.txt
@@ -12,6 +12,7 @@ add_library(rdr STATIC
TLSException.cxx
TLSInStream.cxx
TLSOutStream.cxx
+ TLSSocket.cxx
ZlibInStream.cxx
ZlibOutStream.cxx)
@@ -27,7 +28,6 @@ endif()
if (NETTLE_FOUND)
target_include_directories(rdr SYSTEM PUBLIC ${NETTLE_INCLUDE_DIRS})
target_link_libraries(rdr ${NETTLE_LIBRARIES})
- target_link_directories(rdr PUBLIC ${NETTLE_LIBRARY_DIRS})
endif()
if(WIN32)
target_link_libraries(rdr ws2_32)
diff --git a/common/rdr/InStream.h b/common/rdr/InStream.h
index 5bec276d..7ad4996f 100644
--- a/common/rdr/InStream.h
+++ b/common/rdr/InStream.h
@@ -187,9 +187,7 @@ namespace rdr {
private:
const uint8_t* restorePoint;
-#ifdef RFB_INSTREAM_CHECK
size_t checkedBytes;
-#endif
inline void check(size_t bytes) {
#ifdef RFB_INSTREAM_CHECK
@@ -209,11 +207,7 @@ namespace rdr {
protected:
- InStream() : restorePoint(nullptr)
-#ifdef RFB_INSTREAM_CHECK
- ,checkedBytes(0)
-#endif
- {}
+ InStream() : restorePoint(nullptr), checkedBytes(0) {}
const uint8_t* ptr;
const uint8_t* end;
};
diff --git a/common/rdr/TLSException.cxx b/common/rdr/TLSException.cxx
index a1896af4..8c93a3d3 100644
--- a/common/rdr/TLSException.cxx
+++ b/common/rdr/TLSException.cxx
@@ -35,11 +35,28 @@
using namespace rdr;
#ifdef HAVE_GNUTLS
-tls_error::tls_error(const char* s, int err_) noexcept
+tls_error::tls_error(const char* s, int err_, int alert_) noexcept
: std::runtime_error(core::format("%s: %s (%d)", s,
- gnutls_strerror(err_), err_)),
- err(err_)
+ strerror(err_, alert_), err_)),
+ err(err_), alert(alert_)
{
}
+
+const char* tls_error::strerror(int err_, int alert_) const noexcept
+{
+ const char* msg;
+
+ msg = nullptr;
+
+ if ((alert_ != -1) &&
+ ((err_ == GNUTLS_E_WARNING_ALERT_RECEIVED) ||
+ (err_ == GNUTLS_E_FATAL_ALERT_RECEIVED)))
+ msg = gnutls_alert_get_name((gnutls_alert_description_t)alert_);
+
+ if (msg == nullptr)
+ msg = gnutls_strerror(err_);
+
+ return msg;
+}
#endif /* HAVE_GNUTLS */
diff --git a/common/rdr/TLSException.h b/common/rdr/TLSException.h
index b35a675f..75ee94f5 100644
--- a/common/rdr/TLSException.h
+++ b/common/rdr/TLSException.h
@@ -27,8 +27,10 @@ namespace rdr {
class tls_error : public std::runtime_error {
public:
- int err;
- tls_error(const char* s, int err_) noexcept;
+ int err, alert;
+ tls_error(const char* s, int err_, int alert_=-1) noexcept;
+ private:
+ const char* strerror(int err_, int alert_) const noexcept;
};
}
diff --git a/common/rdr/TLSInStream.cxx b/common/rdr/TLSInStream.cxx
index 223e3ee6..3e5ea2be 100644
--- a/common/rdr/TLSInStream.cxx
+++ b/common/rdr/TLSInStream.cxx
@@ -1,7 +1,7 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (C) 2005 Martin Koegler
* Copyright (C) 2010 TigerVNC Team
- * Copyright (C) 2012-2021 Pierre Ossman for Cendio AB
+ * Copyright 2012-2025 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,73 +23,25 @@
#include <config.h>
#endif
-#include <core/Exception.h>
-#include <core/LogWriter.h>
-
-#include <rdr/TLSException.h>
#include <rdr/TLSInStream.h>
+#include <rdr/TLSSocket.h>
-#include <errno.h>
+#ifdef HAVE_GNUTLS
-#ifdef HAVE_GNUTLS
using namespace rdr;
-static core::LogWriter vlog("TLSInStream");
-
-ssize_t TLSInStream::pull(gnutls_transport_ptr_t str, void* data, size_t size)
-{
- TLSInStream* self= (TLSInStream*) str;
- InStream *in = self->in;
-
- self->streamEmpty = false;
- self->saved_exception = nullptr;
-
- try {
- if (!in->hasData(1)) {
- self->streamEmpty = true;
- gnutls_transport_set_errno(self->session, EAGAIN);
- return -1;
- }
-
- if (in->avail() < size)
- size = in->avail();
-
- in->readBytes((uint8_t*)data, size);
- } catch (end_of_stream&) {
- return 0;
- } catch (std::exception& e) {
- core::socket_error* se;
- vlog.error("Failure reading TLS data: %s", e.what());
- se = dynamic_cast<core::socket_error*>(&e);
- if (se)
- gnutls_transport_set_errno(self->session, se->err);
- else
- gnutls_transport_set_errno(self->session, EINVAL);
- self->saved_exception = std::current_exception();
- return -1;
- }
-
- return size;
-}
-
-TLSInStream::TLSInStream(InStream* _in, gnutls_session_t _session)
- : session(_session), in(_in)
+TLSInStream::TLSInStream(TLSSocket* sock_)
+ : sock(sock_)
{
- gnutls_transport_ptr_t recv, send;
-
- gnutls_transport_set_pull_function(session, pull);
- gnutls_transport_get_ptr2(session, &recv, &send);
- gnutls_transport_set_ptr2(session, this, send);
}
TLSInStream::~TLSInStream()
{
- gnutls_transport_set_pull_function(session, nullptr);
}
bool TLSInStream::fillBuffer()
{
- size_t n = readTLS((uint8_t*) end, availSpace());
+ size_t n = sock->readTLS((uint8_t*) end, availSpace());
if (n == 0)
return false;
end += n;
@@ -97,35 +49,4 @@ bool TLSInStream::fillBuffer()
return true;
}
-size_t TLSInStream::readTLS(uint8_t* buf, size_t len)
-{
- int n;
-
- while (true) {
- streamEmpty = false;
- n = gnutls_record_recv(session, (void *) buf, len);
- if (n == GNUTLS_E_INTERRUPTED || n == GNUTLS_E_AGAIN) {
- // GnuTLS returns GNUTLS_E_AGAIN for a bunch of other scenarios
- // other than the pull function returning EAGAIN, so we have to
- // double check that the underlying stream really is empty
- if (!streamEmpty)
- continue;
- else
- return 0;
- }
- break;
- };
-
- if (n == GNUTLS_E_PULL_ERROR)
- std::rethrow_exception(saved_exception);
-
- if (n < 0)
- throw tls_error("readTLS", n);
-
- if (n == 0)
- throw end_of_stream();
-
- return n;
-}
-
#endif
diff --git a/common/rdr/TLSInStream.h b/common/rdr/TLSInStream.h
index bc9c74ba..94266e50 100644
--- a/common/rdr/TLSInStream.h
+++ b/common/rdr/TLSInStream.h
@@ -1,5 +1,6 @@
/* Copyright (C) 2005 Martin Koegler
* Copyright (C) 2010 TigerVNC Team
+ * Copyright 2012-2025 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,28 +23,25 @@
#ifdef HAVE_GNUTLS
-#include <gnutls/gnutls.h>
#include <rdr/BufferedInStream.h>
namespace rdr {
+ class TLSSocket;
+
class TLSInStream : public BufferedInStream {
public:
- TLSInStream(InStream* in, gnutls_session_t session);
+ TLSInStream(TLSSocket* sock);
virtual ~TLSInStream();
private:
bool fillBuffer() override;
- size_t readTLS(uint8_t* buf, size_t len);
- static ssize_t pull(gnutls_transport_ptr_t str, void* data, size_t size);
-
- gnutls_session_t session;
- InStream* in;
- bool streamEmpty;
- std::exception_ptr saved_exception;
+ TLSSocket* sock;
};
-};
+
+}
#endif
+
#endif
diff --git a/common/rdr/TLSOutStream.cxx b/common/rdr/TLSOutStream.cxx
index c3ae2d0a..ba9d182f 100644
--- a/common/rdr/TLSOutStream.cxx
+++ b/common/rdr/TLSOutStream.cxx
@@ -1,7 +1,7 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (C) 2005 Martin Koegler
* Copyright (C) 2010 TigerVNC Team
- * Copyright (C) 2012-2021 Pierre Ossman for Cendio AB
+ * Copyright 2012-2025 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,103 +23,42 @@
#include <config.h>
#endif
-#include <core/Exception.h>
-#include <core/LogWriter.h>
-
-#include <rdr/TLSException.h>
#include <rdr/TLSOutStream.h>
-
-#include <errno.h>
+#include <rdr/TLSSocket.h>
#ifdef HAVE_GNUTLS
-using namespace rdr;
-static core::LogWriter vlog("TLSOutStream");
+using namespace rdr;
-ssize_t TLSOutStream::push(gnutls_transport_ptr_t str, const void* data,
- size_t size)
+TLSOutStream::TLSOutStream(TLSSocket* sock_)
+ : sock(sock_)
{
- TLSOutStream* self= (TLSOutStream*) str;
- OutStream *out = self->out;
-
- self->saved_exception = nullptr;
-
- try {
- out->writeBytes((const uint8_t*)data, size);
- out->flush();
- } catch (std::exception& e) {
- core::socket_error* se;
- vlog.error("Failure sending TLS data: %s", e.what());
- se = dynamic_cast<core::socket_error*>(&e);
- if (se)
- gnutls_transport_set_errno(self->session, se->err);
- else
- gnutls_transport_set_errno(self->session, EINVAL);
- self->saved_exception = std::current_exception();
- return -1;
- }
-
- return size;
-}
-
-TLSOutStream::TLSOutStream(OutStream* _out, gnutls_session_t _session)
- : session(_session), out(_out)
-{
- gnutls_transport_ptr_t recv, send;
-
- gnutls_transport_set_push_function(session, push);
- gnutls_transport_get_ptr2(session, &recv, &send);
- gnutls_transport_set_ptr2(session, recv, this);
}
TLSOutStream::~TLSOutStream()
{
-#if 0
- try {
-// flush();
- } catch (Exception&) {
- }
-#endif
- gnutls_transport_set_push_function(session, nullptr);
}
void TLSOutStream::flush()
{
BufferedOutStream::flush();
- out->flush();
+ sock->out->flush();
}
void TLSOutStream::cork(bool enable)
{
BufferedOutStream::cork(enable);
- out->cork(enable);
+ sock->out->cork(enable);
}
bool TLSOutStream::flushBuffer()
{
while (sentUpTo < ptr) {
- size_t n = writeTLS(sentUpTo, ptr - sentUpTo);
+ size_t n = sock->writeTLS(sentUpTo, ptr - sentUpTo);
sentUpTo += n;
}
return true;
}
-size_t TLSOutStream::writeTLS(const uint8_t* data, size_t length)
-{
- int n;
-
- n = gnutls_record_send(session, data, length);
- if (n == GNUTLS_E_INTERRUPTED || n == GNUTLS_E_AGAIN)
- return 0;
-
- if (n == GNUTLS_E_PUSH_ERROR)
- std::rethrow_exception(saved_exception);
-
- if (n < 0)
- throw tls_error("writeTLS", n);
-
- return n;
-}
-
#endif
diff --git a/common/rdr/TLSOutStream.h b/common/rdr/TLSOutStream.h
index 0ae9c460..aa9572ba 100644
--- a/common/rdr/TLSOutStream.h
+++ b/common/rdr/TLSOutStream.h
@@ -21,14 +21,16 @@
#define __RDR_TLSOUTSTREAM_H__
#ifdef HAVE_GNUTLS
-#include <gnutls/gnutls.h>
+
#include <rdr/BufferedOutStream.h>
namespace rdr {
+ class TLSSocket;
+
class TLSOutStream : public BufferedOutStream {
public:
- TLSOutStream(OutStream* out, gnutls_session_t session);
+ TLSOutStream(TLSSocket* out);
virtual ~TLSOutStream();
void flush() override;
@@ -36,15 +38,12 @@ namespace rdr {
private:
bool flushBuffer() override;
- size_t writeTLS(const uint8_t* data, size_t length);
- static ssize_t push(gnutls_transport_ptr_t str, const void* data, size_t size);
-
- gnutls_session_t session;
- OutStream* out;
- std::exception_ptr saved_exception;
+ TLSSocket* sock;
};
-};
+
+}
#endif
+
#endif
diff --git a/common/rdr/TLSSocket.cxx b/common/rdr/TLSSocket.cxx
new file mode 100644
index 00000000..a29e41c1
--- /dev/null
+++ b/common/rdr/TLSSocket.cxx
@@ -0,0 +1,228 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ * Copyright (C) 2005 Martin Koegler
+ * Copyright (C) 2010 TigerVNC Team
+ * Copyright (C) 2012-2025 Pierre Ossman for Cendio AB
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <core/Exception.h>
+#include <core/LogWriter.h>
+
+#include <rdr/InStream.h>
+#include <rdr/OutStream.h>
+#include <rdr/TLSException.h>
+#include <rdr/TLSSocket.h>
+
+#include <errno.h>
+
+#ifdef HAVE_GNUTLS
+
+using namespace rdr;
+
+static core::LogWriter vlog("TLSSocket");
+
+TLSSocket::TLSSocket(InStream* in_, OutStream* out_,
+ gnutls_session_t session_)
+ : session(session_), in(in_), out(out_), tlsin(this), tlsout(this)
+{
+ gnutls_transport_set_pull_function(
+ session, [](gnutls_transport_ptr_t sock, void* data, size_t size) {
+ return ((TLSSocket*)sock)->pull(data, size);
+ });
+ gnutls_transport_set_push_function(
+ session, [](gnutls_transport_ptr_t sock, const void* data, size_t size) {
+ return ((TLSSocket*)sock)->push(data, size);
+ });
+ gnutls_transport_set_ptr(session, this);
+}
+
+TLSSocket::~TLSSocket()
+{
+ gnutls_transport_set_pull_function(session, nullptr);
+ gnutls_transport_set_push_function(session, nullptr);
+ gnutls_transport_set_ptr(session, nullptr);
+}
+
+bool TLSSocket::handshake()
+{
+ int err;
+
+ err = gnutls_handshake(session);
+ if (err != GNUTLS_E_SUCCESS) {
+ gnutls_alert_description_t alert;
+ const char* msg;
+
+ if ((err == GNUTLS_E_PULL_ERROR) || (err == GNUTLS_E_PUSH_ERROR))
+ std::rethrow_exception(saved_exception);
+
+ alert = gnutls_alert_get(session);
+ msg = nullptr;
+
+ if ((err == GNUTLS_E_WARNING_ALERT_RECEIVED) ||
+ (err == GNUTLS_E_FATAL_ALERT_RECEIVED))
+ msg = gnutls_alert_get_name(alert);
+
+ if (msg == nullptr)
+ msg = gnutls_strerror(err);
+
+ if (!gnutls_error_is_fatal(err)) {
+ vlog.debug("Deferring completion of TLS handshake: %s", msg);
+ return false;
+ }
+
+ vlog.error("TLS Handshake failed: %s\n", msg);
+ gnutls_alert_send_appropriate(session, err);
+ throw rdr::tls_error("TLS Handshake failed", err, alert);
+ }
+
+ return true;
+}
+
+void TLSSocket::shutdown()
+{
+ int ret;
+
+ try {
+ if (tlsout.hasBufferedData()) {
+ tlsout.cork(false);
+ tlsout.flush();
+ if (tlsout.hasBufferedData())
+ vlog.error("Failed to flush remaining socket data on close");
+ }
+ } catch (std::exception& e) {
+ vlog.error("Failed to flush remaining socket data on close: %s", e.what());
+ }
+
+ // FIXME: We can't currently wait for the response, so we only send
+ // our close and hope for the best
+ ret = gnutls_bye(session, GNUTLS_SHUT_WR);
+ if ((ret != GNUTLS_E_SUCCESS) && (ret != GNUTLS_E_INVALID_SESSION))
+ vlog.error("TLS shutdown failed: %s", gnutls_strerror(ret));
+}
+
+size_t TLSSocket::readTLS(uint8_t* buf, size_t len)
+{
+ int n;
+
+ while (true) {
+ streamEmpty = false;
+ n = gnutls_record_recv(session, (void *) buf, len);
+ if (n == GNUTLS_E_INTERRUPTED || n == GNUTLS_E_AGAIN) {
+ // GnuTLS returns GNUTLS_E_AGAIN for a bunch of other scenarios
+ // other than the pull function returning EAGAIN, so we have to
+ // double check that the underlying stream really is empty
+ if (!streamEmpty)
+ continue;
+ else
+ return 0;
+ }
+ break;
+ };
+
+ if (n == GNUTLS_E_PULL_ERROR)
+ std::rethrow_exception(saved_exception);
+
+ if (n < 0) {
+ gnutls_alert_send_appropriate(session, n);
+ throw tls_error("readTLS", n, gnutls_alert_get(session));
+ }
+
+ if (n == 0)
+ throw end_of_stream();
+
+ return n;
+}
+
+size_t TLSSocket::writeTLS(const uint8_t* data, size_t length)
+{
+ int n;
+
+ n = gnutls_record_send(session, data, length);
+ if (n == GNUTLS_E_INTERRUPTED || n == GNUTLS_E_AGAIN)
+ return 0;
+
+ if (n == GNUTLS_E_PUSH_ERROR)
+ std::rethrow_exception(saved_exception);
+
+ if (n < 0) {
+ gnutls_alert_send_appropriate(session, n);
+ throw tls_error("writeTLS", n, gnutls_alert_get(session));
+ }
+
+ return n;
+}
+
+ssize_t TLSSocket::pull(void* data, size_t size)
+{
+ streamEmpty = false;
+ saved_exception = nullptr;
+
+ try {
+ if (!in->hasData(1)) {
+ streamEmpty = true;
+ gnutls_transport_set_errno(session, EAGAIN);
+ return -1;
+ }
+
+ if (in->avail() < size)
+ size = in->avail();
+
+ in->readBytes((uint8_t*)data, size);
+ } catch (end_of_stream&) {
+ return 0;
+ } catch (std::exception& e) {
+ core::socket_error* se;
+ vlog.error("Failure reading TLS data: %s", e.what());
+ se = dynamic_cast<core::socket_error*>(&e);
+ if (se)
+ gnutls_transport_set_errno(session, se->err);
+ else
+ gnutls_transport_set_errno(session, EINVAL);
+ saved_exception = std::current_exception();
+ return -1;
+ }
+
+ return size;
+}
+
+ssize_t TLSSocket::push(const void* data, size_t size)
+{
+ saved_exception = nullptr;
+
+ try {
+ out->writeBytes((const uint8_t*)data, size);
+ out->flush();
+ } catch (std::exception& e) {
+ core::socket_error* se;
+ vlog.error("Failure sending TLS data: %s", e.what());
+ se = dynamic_cast<core::socket_error*>(&e);
+ if (se)
+ gnutls_transport_set_errno(session, se->err);
+ else
+ gnutls_transport_set_errno(session, EINVAL);
+ saved_exception = std::current_exception();
+ return -1;
+ }
+
+ return size;
+}
+
+#endif
diff --git a/common/rdr/TLSSocket.h b/common/rdr/TLSSocket.h
new file mode 100644
index 00000000..ca29f8bc
--- /dev/null
+++ b/common/rdr/TLSSocket.h
@@ -0,0 +1,81 @@
+/* Copyright (C) 2005 Martin Koegler
+ * Copyright (C) 2010 TigerVNC Team
+ * Copyright 2012-2025 Pierre Ossman for Cendio AB
+ *
+ * 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.
+ */
+
+#ifndef __RDR_TLSSOCKET_H__
+#define __RDR_TLSSOCKET_H__
+
+#ifdef HAVE_GNUTLS
+
+#include <exception>
+
+#include <gnutls/gnutls.h>
+
+#include <rdr/TLSInStream.h>
+#include <rdr/TLSOutStream.h>
+
+namespace rdr {
+
+ class InStream;
+ class OutStream;
+
+ class TLSInStream;
+ class TLSOutStream;
+
+ class TLSSocket {
+ public:
+ TLSSocket(InStream* in, OutStream* out, gnutls_session_t session);
+ virtual ~TLSSocket();
+
+ TLSInStream& inStream() { return tlsin; }
+ TLSOutStream& outStream() { return tlsout; }
+
+ bool handshake();
+ void shutdown();
+
+ protected:
+ /* Used by the stream classes */
+ size_t readTLS(uint8_t* buf, size_t len);
+ size_t writeTLS(const uint8_t* data, size_t length);
+
+ friend TLSInStream;
+ friend TLSOutStream;
+
+ private:
+ ssize_t pull(void* data, size_t size);
+ ssize_t push(const void* data, size_t size);
+
+ gnutls_session_t session;
+
+ InStream* in;
+ OutStream* out;
+
+ TLSInStream tlsin;
+ TLSOutStream tlsout;
+
+ bool streamEmpty;
+
+ std::exception_ptr saved_exception;
+ };
+
+}
+
+#endif
+
+#endif
diff --git a/common/rfb/CConnection.cxx b/common/rfb/CConnection.cxx
index cbb62fe3..bbeef385 100644
--- a/common/rfb/CConnection.cxx
+++ b/common/rfb/CConnection.cxx
@@ -37,6 +37,7 @@
#include <rfb/CMsgReader.h>
#include <rfb/CMsgWriter.h>
#include <rfb/CSecurity.h>
+#include <rfb/Cursor.h>
#include <rfb/Decoder.h>
#include <rfb/KeysymStr.h>
#include <rfb/PixelBuffer.h>
@@ -379,7 +380,6 @@ void CConnection::securityCompleted()
reader_ = new CMsgReader(this, is);
writer_ = new CMsgWriter(&server, os);
vlog.debug("Authentication success!");
- authSuccess();
writer_->writeClientInit(shared);
}
@@ -410,7 +410,7 @@ void CConnection::setDesktopSize(int w, int h)
{
decoder.flush();
- CMsgHandler::setDesktopSize(w,h);
+ server.setDimensions(w, h);
if (continuousUpdates)
writer()->writeEnableContinuousUpdates(true, 0, 0,
@@ -430,7 +430,10 @@ void CConnection::setExtendedDesktopSize(unsigned reason,
{
decoder.flush();
- CMsgHandler::setExtendedDesktopSize(reason, result, w, h, layout);
+ server.supportsSetDesktopSize = true;
+
+ if ((reason != reasonClient) || (result == resultSuccess))
+ server.setDimensions(w, h, layout);
if ((reason == reasonClient) && (result != resultSuccess)) {
vlog.error("SetDesktopSize failed: %d", result);
@@ -448,9 +451,41 @@ void CConnection::setExtendedDesktopSize(unsigned reason,
assert(framebuffer->height() == server.height());
}
+void CConnection::setCursor(int width, int height,
+ const core::Point& hotspot,
+ const uint8_t* data)
+{
+ Cursor cursor(width, height, hotspot, data);
+ server.setCursor(cursor);
+}
+
+void CConnection::setCursorPos(const core::Point& /*pos*/)
+{
+}
+
+void CConnection::setName(const char* name)
+{
+ server.setName(name);
+}
+
+void CConnection::fence(uint32_t flags, unsigned len, const uint8_t data[])
+{
+ server.supportsFence = true;
+
+ if (flags & fenceFlagRequest) {
+ // FIXME: We handle everything synchronously, and we assume anything
+ // using us also does so, which means we automatically handle
+ // these flags
+ flags = flags & (fenceFlagBlockBefore | fenceFlagBlockAfter);
+
+ writer()->writeFence(flags, len, data);
+ return;
+ }
+}
+
void CConnection::endOfContinuousUpdates()
{
- CMsgHandler::endOfContinuousUpdates();
+ server.supportsContinuousUpdates = true;
// We've gotten the marker for a format change, so make the pending
// one active
@@ -464,11 +499,23 @@ void CConnection::endOfContinuousUpdates()
}
}
+void CConnection::supportsQEMUKeyEvent()
+{
+ server.supportsQEMUKeyEvent = true;
+}
+
+void CConnection::supportsExtendedMouseButtons()
+{
+ server.supportsExtendedMouseButtons = true;
+}
+
void CConnection::serverInit(int width, int height,
const PixelFormat& pf,
const char* name)
{
- CMsgHandler::serverInit(width, height, pf, name);
+ server.setDimensions(width, height);
+ server.setPF(pf);
+ server.setName(name);
state_ = RFBSTATE_NORMAL;
vlog.debug("Initialisation done");
@@ -502,8 +549,6 @@ bool CConnection::readAndDecodeRect(const core::Rect& r, int encoding,
void CConnection::framebufferUpdateStart()
{
- CMsgHandler::framebufferUpdateStart();
-
assert(framebuffer != nullptr);
// Note: This might not be true if continuous updates are supported
@@ -516,8 +561,6 @@ void CConnection::framebufferUpdateEnd()
{
decoder.flush();
- CMsgHandler::framebufferUpdateEnd();
-
// A format change has been scheduled and we are now past the update
// with the old format. Time to active the new one.
if (pendingPFChange && !continuousUpdates) {
@@ -543,6 +586,13 @@ bool CConnection::dataRect(const core::Rect& r, int encoding)
return decoder.decodeRect(r, encoding, framebuffer);
}
+void CConnection::setColourMapEntries(int /*firstColour*/,
+ int /*nColours*/,
+ uint16_t* /*rgbs*/)
+{
+ vlog.error("Invalid SetColourMapEntries from server!");
+}
+
void CConnection::serverCutText(const char* str)
{
hasLocalClipboard = false;
@@ -553,12 +603,53 @@ void CConnection::serverCutText(const char* str)
handleClipboardAnnounce(true);
}
+void CConnection::setLEDState(unsigned int state)
+{
+ server.setLEDState(state);
+}
+
void CConnection::handleClipboardCaps(uint32_t flags,
const uint32_t* lengths)
{
+ int i;
uint32_t sizes[] = { 0 };
- CMsgHandler::handleClipboardCaps(flags, lengths);
+ vlog.debug("Got server clipboard capabilities:");
+ for (i = 0;i < 16;i++) {
+ if (flags & (1 << i)) {
+ const char *type;
+
+ switch (1 << i) {
+ case clipboardUTF8:
+ type = "Plain text";
+ break;
+ case clipboardRTF:
+ type = "Rich text";
+ break;
+ case clipboardHTML:
+ type = "HTML";
+ break;
+ case clipboardDIB:
+ type = "Images";
+ break;
+ case clipboardFiles:
+ type = "Files";
+ break;
+ default:
+ vlog.debug(" Unknown format 0x%x", 1 << i);
+ continue;
+ }
+
+ if (lengths[i] == 0)
+ vlog.debug(" %s (only notify)", type);
+ else {
+ vlog.debug(" %s (automatically send up to %s)",
+ type, core::iecPrefix(lengths[i], "B").c_str());
+ }
+ }
+ }
+
+ server.setClipboardCaps(flags, lengths);
writer()->writeClipboardCaps(rfb::clipboardUTF8 |
rfb::clipboardRequest |
@@ -620,10 +711,6 @@ void CConnection::handleClipboardProvide(uint32_t flags,
handleClipboardData(serverClipboard.c_str());
}
-void CConnection::authSuccess()
-{
-}
-
void CConnection::initDone()
{
}
@@ -820,6 +907,11 @@ void CConnection::setCompressLevel(int level)
encodingChange = true;
}
+int CConnection::getCompressLevel()
+{
+ return compressLevel;
+}
+
void CConnection::setQualityLevel(int level)
{
if (qualityLevel == level)
@@ -829,6 +921,11 @@ void CConnection::setQualityLevel(int level)
encodingChange = true;
}
+int CConnection::getQualityLevel()
+{
+ return qualityLevel;
+}
+
void CConnection::setPF(const PixelFormat& pf)
{
if (server.pf() == pf && !formatChange)
@@ -843,19 +940,6 @@ bool CConnection::isSecure() const
return csecurity ? csecurity->isSecure() : false;
}
-void CConnection::fence(uint32_t flags, unsigned len, const uint8_t data[])
-{
- CMsgHandler::fence(flags, len, data);
-
- if (!(flags & fenceFlagRequest))
- return;
-
- // We cannot guarantee any synchronisation at this level
- flags = 0;
-
- writer()->writeFence(flags, len, data);
-}
-
// requestNewUpdate() requests an update from the server, having set the
// format and encoding appropriately.
void CConnection::requestNewUpdate()
diff --git a/common/rfb/CConnection.h b/common/rfb/CConnection.h
index 9ffbd2c5..b28c5aa9 100644
--- a/common/rfb/CConnection.h
+++ b/common/rfb/CConnection.h
@@ -105,89 +105,6 @@ namespace rfb {
// connection
void close();
-
- // Methods overridden from CMsgHandler
-
- // Note: These must be called by any deriving classes
-
- void setDesktopSize(int w, int h) override;
- void setExtendedDesktopSize(unsigned reason, unsigned result,
- int w, int h,
- const ScreenSet& layout) override;
-
- void endOfContinuousUpdates() override;
-
- void serverInit(int width, int height, const PixelFormat& pf,
- const char* name) override;
-
- bool readAndDecodeRect(const core::Rect& r, int encoding,
- ModifiablePixelBuffer* pb) override;
-
- void framebufferUpdateStart() override;
- void framebufferUpdateEnd() override;
- bool dataRect(const core::Rect& r, int encoding) override;
-
- void serverCutText(const char* str) override;
-
- void handleClipboardCaps(uint32_t flags,
- const uint32_t* lengths) override;
- void handleClipboardRequest(uint32_t flags) override;
- void handleClipboardPeek() override;
- void handleClipboardNotify(uint32_t flags) override;
- void handleClipboardProvide(uint32_t flags, const size_t* lengths,
- const uint8_t* const* data) override;
-
-
- // Methods to be overridden in a derived class
-
- // getUserPasswd() gets the username and password. This might
- // involve a dialog, getpass(), etc. The user buffer pointer can be
- // null, in which case no user name will be retrieved.
- virtual void getUserPasswd(bool secure, std::string* user,
- std::string* password) = 0;
-
- // showMsgBox() displays a message box with the specified style and
- // contents. The return value is true if the user clicked OK/Yes.
- virtual bool showMsgBox(MsgBoxFlags flags, const char *title,
- const char *text) = 0;
-
- // authSuccess() is called when authentication has succeeded.
- virtual void authSuccess();
-
- // initDone() is called when the connection is fully established
- // and standard messages can be sent. This is called before the
- // initial FramebufferUpdateRequest giving a derived class the
- // chance to modify pixel format and settings. The derived class
- // must also make sure it has provided a valid framebuffer before
- // returning.
- virtual void initDone() = 0;
-
- // resizeFramebuffer() is called whenever the framebuffer
- // dimensions or the screen layout changes. A subclass must make
- // sure the pixel buffer has been updated once this call returns.
- virtual void resizeFramebuffer();
-
- // handleClipboardRequest() is called whenever the server requests
- // the client to send over its clipboard data. It will only be
- // called after the client has first announced a clipboard change
- // via announceClipboard().
- virtual void handleClipboardRequest();
-
- // handleClipboardAnnounce() is called to indicate a change in the
- // clipboard on the server. Call requestClipboard() to access the
- // actual data.
- virtual void handleClipboardAnnounce(bool available);
-
- // handleClipboardData() is called when the server has sent over
- // the clipboard data as a result of a previous call to
- // requestClipboard(). Note that this function might never be
- // called if the clipboard data was no longer available when the
- // server received the request.
- virtual void handleClipboardData(const char* data);
-
-
- // Other methods
-
// requestClipboard() will result in a request to the server to
// transfer its clipboard data. A call to handleClipboardData()
// will be made once the data is available.
@@ -227,7 +144,9 @@ namespace rfb {
// setCompressLevel()/setQualityLevel() controls the encoding hints
// sent to the server
void setCompressLevel(int level);
+ int getCompressLevel();
void setQualityLevel(int level);
+ int getQualityLevel();
// setPF() controls the pixel format requested from the server.
// server.pf() will automatically be adjusted once the new format
// is active.
@@ -260,8 +179,107 @@ namespace rfb {
stateEnum state() { return state_; }
+ // Methods used by SSecurity classes
+
+ // getUserPasswd() gets the username and password. This might
+ // involve a dialog, getpass(), etc. The user buffer pointer can be
+ // null, in which case no user name will be retrieved.
+ virtual void getUserPasswd(bool secure, std::string* user,
+ std::string* password) = 0;
+
+ // showMsgBox() displays a message box with the specified style and
+ // contents. The return value is true if the user clicked OK/Yes.
+ virtual bool showMsgBox(MsgBoxFlags flags, const char *title,
+ const char *text) = 0;
+
+ protected:
+
+ // Methods overridden from CMsgHandler
+
+ // Note: These must be called by any deriving classes
+
+ void setDesktopSize(int w, int h) override;
+ void setExtendedDesktopSize(unsigned reason, unsigned result,
+ int w, int h,
+ const ScreenSet& layout) override;
+
+ void setCursor(int width, int height, const core::Point& hotspot,
+ const uint8_t* data) override;
+ void setCursorPos(const core::Point& pos) override;
+
+ void setName(const char* name) override;
+
+ void fence(uint32_t flags, unsigned len, const uint8_t data[]) override;
+
+ void endOfContinuousUpdates() override;
+
+ void supportsQEMUKeyEvent() override;
+
+ void supportsExtendedMouseButtons() override;
+
+ void serverInit(int width, int height, const PixelFormat& pf,
+ const char* name) override;
+
+ bool readAndDecodeRect(const core::Rect& r, int encoding,
+ ModifiablePixelBuffer* pb) override;
+
+ void framebufferUpdateStart() override;
+ void framebufferUpdateEnd() override;
+ bool dataRect(const core::Rect& r, int encoding) override;
+
+ void setColourMapEntries(int firstColour, int nColours,
+ uint16_t* rgbs) override;
+
+ void serverCutText(const char* str) override;
+
+ void setLEDState(unsigned int state) override;
+
+ void handleClipboardCaps(uint32_t flags,
+ const uint32_t* lengths) override;
+ void handleClipboardRequest(uint32_t flags) override;
+ void handleClipboardPeek() override;
+ void handleClipboardNotify(uint32_t flags) override;
+ void handleClipboardProvide(uint32_t flags, const size_t* lengths,
+ const uint8_t* const* data) override;
+
+
+ // Methods to be overridden in a derived class
+
+ // initDone() is called when the connection is fully established
+ // and standard messages can be sent. This is called before the
+ // initial FramebufferUpdateRequest giving a derived class the
+ // chance to modify pixel format and settings. The derived class
+ // must also make sure it has provided a valid framebuffer before
+ // returning.
+ virtual void initDone() = 0;
+
+ // resizeFramebuffer() is called whenever the framebuffer
+ // dimensions or the screen layout changes. A subclass must make
+ // sure the pixel buffer has been updated once this call returns.
+ virtual void resizeFramebuffer();
+
+ // handleClipboardRequest() is called whenever the server requests
+ // the client to send over its clipboard data. It will only be
+ // called after the client has first announced a clipboard change
+ // via announceClipboard().
+ virtual void handleClipboardRequest();
+
+ // handleClipboardAnnounce() is called to indicate a change in the
+ // clipboard on the server. Call requestClipboard() to access the
+ // actual data.
+ virtual void handleClipboardAnnounce(bool available);
+
+ // handleClipboardData() is called when the server has sent over
+ // the clipboard data as a result of a previous call to
+ // requestClipboard(). Note that this function might never be
+ // called if the clipboard data was no longer available when the
+ // server received the request.
+ virtual void handleClipboardData(const char* data);
+
+ protected:
CSecurity *csecurity;
SecurityClient security;
+
protected:
void setState(stateEnum s) { state_ = s; }
@@ -279,13 +297,6 @@ namespace rfb {
bool supportsLEDState;
private:
- // This is a default implementation of fences that automatically
- // responds to requests, stating no support for synchronisation.
- // When overriding, call CMsgHandler::fence() directly in order to
- // state correct support for fence flags.
- void fence(uint32_t flags, unsigned len, const uint8_t data[]) override;
-
- private:
bool processVersionMsg();
bool processSecurityTypesMsg();
bool processSecurityMsg();
diff --git a/common/rfb/CMakeLists.txt b/common/rfb/CMakeLists.txt
index 2b5eea68..d7467421 100644
--- a/common/rfb/CMakeLists.txt
+++ b/common/rfb/CMakeLists.txt
@@ -3,7 +3,6 @@ add_library(rfb STATIC
Blacklist.cxx
Congestion.cxx
CConnection.cxx
- CMsgHandler.cxx
CMsgReader.cxx
CMsgWriter.cxx
CSecurityPlain.cxx
@@ -32,7 +31,6 @@ add_library(rfb STATIC
RawDecoder.cxx
RawEncoder.cxx
SConnection.cxx
- SMsgHandler.cxx
SMsgReader.cxx
SMsgWriter.cxx
ServerCore.cxx
@@ -69,7 +67,6 @@ if(ENABLE_H264 AND NOT H264_LIBS STREQUAL "NONE")
endif()
target_include_directories(rfb SYSTEM PUBLIC ${H264_INCLUDE_DIRS})
target_link_libraries(rfb ${H264_LIBRARIES})
- target_link_directories(rfb PUBLIC ${H264_LIBRARY_DIRS})
endif()
if(WIN32)
@@ -78,8 +75,9 @@ if(WIN32)
endif(WIN32)
if(UNIX AND NOT APPLE)
- target_sources(rfb PRIVATE UnixPasswordValidator.cxx pam.c)
- target_link_libraries(rfb ${PAM_LIBS})
+ target_sources(rfb PRIVATE UnixPasswordValidator.cxx)
+ target_include_directories(rfb SYSTEM PRIVATE ${PAM_INCLUDE_DIRS})
+ target_link_libraries(rfb ${PAM_LIBRARIES})
endif()
if(GNUTLS_FOUND)
@@ -95,12 +93,8 @@ endif()
if (NETTLE_FOUND)
target_sources(rfb PRIVATE CSecurityDH.cxx CSecurityMSLogonII.cxx
CSecurityRSAAES.cxx SSecurityRSAAES.cxx)
- target_include_directories(rfb SYSTEM PUBLIC ${NETTLE_INCLUDE_DIRS}
- ${GMP_INCLUDE_DIRS})
- target_link_libraries(rfb ${HOGWEED_LIBRARIES}
- ${NETTLE_LIBRARIES} ${GMP_LIBRARIES})
- target_link_directories(rfb PUBLIC ${HOGWEED_LIBRARY_DIRS}
- ${NETTLE_LIBRARY_DIRS} ${GMP_LIBRARY_DIRS})
+ target_include_directories(rfb SYSTEM PUBLIC ${NETTLE_INCLUDE_DIRS})
+ target_link_libraries(rfb ${HOGWEED_LIBRARIES} ${NETTLE_LIBRARIES})
endif()
if(UNIX)
diff --git a/common/rfb/CMsgHandler.cxx b/common/rfb/CMsgHandler.cxx
deleted file mode 100644
index 2a88d867..00000000
--- a/common/rfb/CMsgHandler.cxx
+++ /dev/null
@@ -1,168 +0,0 @@
-/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2019 Pierre Ossman for Cendio AB
- *
- * 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.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-
-#include <core/LogWriter.h>
-#include <core/string.h>
-
-#include <rfb/CMsgHandler.h>
-#include <rfb/clipboardTypes.h>
-#include <rfb/screenTypes.h>
-
-static core::LogWriter vlog("CMsgHandler");
-
-using namespace rfb;
-
-CMsgHandler::CMsgHandler()
-{
-}
-
-CMsgHandler::~CMsgHandler()
-{
-}
-
-void CMsgHandler::setDesktopSize(int width, int height)
-{
- server.setDimensions(width, height);
-}
-
-void CMsgHandler::setExtendedDesktopSize(unsigned reason, unsigned result,
- int width, int height,
- const ScreenSet& layout)
-{
- server.supportsSetDesktopSize = true;
-
- if ((reason == reasonClient) && (result != resultSuccess))
- return;
-
- server.setDimensions(width, height, layout);
-}
-
-void CMsgHandler::setName(const char* name)
-{
- server.setName(name);
-}
-
-void CMsgHandler::fence(uint32_t /*flags*/, unsigned /*len*/,
- const uint8_t /*data*/ [])
-{
- server.supportsFence = true;
-}
-
-void CMsgHandler::endOfContinuousUpdates()
-{
- server.supportsContinuousUpdates = true;
-}
-
-void CMsgHandler::supportsExtendedMouseButtons()
-{
- server.supportsExtendedMouseButtons = true;
-}
-
-void CMsgHandler::supportsQEMUKeyEvent()
-{
- server.supportsQEMUKeyEvent = true;
-}
-
-void CMsgHandler::serverInit(int width, int height,
- const PixelFormat& pf,
- const char* name)
-{
- server.setDimensions(width, height);
- server.setPF(pf);
- server.setName(name);
-}
-
-void CMsgHandler::framebufferUpdateStart()
-{
-}
-
-void CMsgHandler::framebufferUpdateEnd()
-{
-}
-
-void CMsgHandler::setLEDState(unsigned int state)
-{
- server.setLEDState(state);
-}
-
-void CMsgHandler::handleClipboardCaps(uint32_t flags, const uint32_t* lengths)
-{
- int i;
-
- vlog.debug("Got server clipboard capabilities:");
- for (i = 0;i < 16;i++) {
- if (flags & (1 << i)) {
- const char *type;
-
- switch (1 << i) {
- case clipboardUTF8:
- type = "Plain text";
- break;
- case clipboardRTF:
- type = "Rich text";
- break;
- case clipboardHTML:
- type = "HTML";
- break;
- case clipboardDIB:
- type = "Images";
- break;
- case clipboardFiles:
- type = "Files";
- break;
- default:
- vlog.debug(" Unknown format 0x%x", 1 << i);
- continue;
- }
-
- if (lengths[i] == 0)
- vlog.debug(" %s (only notify)", type);
- else {
- vlog.debug(" %s (automatically send up to %s)",
- type, core::iecPrefix(lengths[i], "B").c_str());
- }
- }
- }
-
- server.setClipboardCaps(flags, lengths);
-}
-
-void CMsgHandler::handleClipboardRequest(uint32_t /*flags*/)
-{
-}
-
-void CMsgHandler::handleClipboardPeek()
-{
-}
-
-void CMsgHandler::handleClipboardNotify(uint32_t /*flags*/)
-{
-}
-
-void CMsgHandler::handleClipboardProvide(uint32_t /*flags*/,
- const size_t* /*lengths*/,
- const uint8_t* const* /*data*/)
-{
-}
diff --git a/common/rfb/CMsgHandler.h b/common/rfb/CMsgHandler.h
index e7ba2abc..d267ae47 100644
--- a/common/rfb/CMsgHandler.h
+++ b/common/rfb/CMsgHandler.h
@@ -33,8 +33,6 @@ namespace core {
struct Rect;
}
-namespace rdr { class InStream; }
-
namespace rfb {
class ModifiablePixelBuffer;
@@ -42,29 +40,23 @@ namespace rfb {
class CMsgHandler {
public:
- CMsgHandler();
- virtual ~CMsgHandler();
-
// The following methods are called as corresponding messages are
- // read. A derived class should override these methods as desired.
- // Note that for the setDesktopSize(), setExtendedDesktopSize(),
- // setName(), serverInit() and handleClipboardCaps() methods, a
- // derived class should call on to CMsgHandler's methods to set the
- // members of "server" appropriately.
+ // read. A derived class must override these methods.
- virtual void setDesktopSize(int w, int h);
+ virtual void setDesktopSize(int w, int h) = 0;
virtual void setExtendedDesktopSize(unsigned reason, unsigned result,
int w, int h,
- const ScreenSet& layout);
+ const ScreenSet& layout) = 0;
virtual void setCursor(int width, int height, const
core::Point& hotspot,
const uint8_t* data) = 0;
virtual void setCursorPos(const core::Point& pos) = 0;
- virtual void setName(const char* name);
- virtual void fence(uint32_t flags, unsigned len, const uint8_t data[]);
- virtual void endOfContinuousUpdates();
- virtual void supportsQEMUKeyEvent();
- virtual void supportsExtendedMouseButtons();
+ virtual void setName(const char* name) = 0;
+ virtual void fence(uint32_t flags, unsigned len,
+ const uint8_t data[]) = 0;
+ virtual void endOfContinuousUpdates() = 0;
+ virtual void supportsQEMUKeyEvent() = 0;
+ virtual void supportsExtendedMouseButtons() = 0;
virtual void serverInit(int width, int height,
const PixelFormat& pf,
const char* name) = 0;
@@ -72,8 +64,8 @@ namespace rfb {
virtual bool readAndDecodeRect(const core::Rect& r, int encoding,
ModifiablePixelBuffer* pb) = 0;
- virtual void framebufferUpdateStart();
- virtual void framebufferUpdateEnd();
+ virtual void framebufferUpdateStart() = 0;
+ virtual void framebufferUpdateEnd() = 0;
virtual bool dataRect(const core::Rect& r, int encoding) = 0;
virtual void setColourMapEntries(int firstColour, int nColours,
@@ -81,16 +73,16 @@ namespace rfb {
virtual void bell() = 0;
virtual void serverCutText(const char* str) = 0;
- virtual void setLEDState(unsigned int state);
+ virtual void setLEDState(unsigned int state) = 0;
virtual void handleClipboardCaps(uint32_t flags,
- const uint32_t* lengths);
- virtual void handleClipboardRequest(uint32_t flags);
- virtual void handleClipboardPeek();
- virtual void handleClipboardNotify(uint32_t flags);
+ const uint32_t* lengths) = 0;
+ virtual void handleClipboardRequest(uint32_t flags) = 0;
+ virtual void handleClipboardPeek() = 0;
+ virtual void handleClipboardNotify(uint32_t flags) = 0;
virtual void handleClipboardProvide(uint32_t flags,
const size_t* lengths,
- const uint8_t* const* data);
+ const uint8_t* const* data) = 0;
ServerParams server;
};
diff --git a/common/rfb/CSecurityTLS.cxx b/common/rfb/CSecurityTLS.cxx
index 44f738b4..6eefe73b 100644
--- a/common/rfb/CSecurityTLS.cxx
+++ b/common/rfb/CSecurityTLS.cxx
@@ -3,7 +3,7 @@
* Copyright (C) 2005 Martin Koegler
* Copyright (C) 2010 TigerVNC Team
* Copyright (C) 2010 m-privacy GmbH
- * Copyright (C) 2012-2021 Pierre Ossman for Cendio AB
+ * Copyright 2012-2025 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -43,8 +43,7 @@
#include <rfb/Exception.h>
#include <rdr/TLSException.h>
-#include <rdr/TLSInStream.h>
-#include <rdr/TLSOutStream.h>
+#include <rdr/TLSSocket.h>
#include <gnutls/x509.h>
@@ -75,7 +74,7 @@ static const char* configdirfn(const char* fn)
CSecurityTLS::CSecurityTLS(CConnection* cc_, bool _anon)
: CSecurity(cc_), session(nullptr),
anon_cred(nullptr), cert_cred(nullptr),
- anon(_anon), tlsis(nullptr), tlsos(nullptr),
+ anon(_anon), tlssock(nullptr),
rawis(nullptr), rawos(nullptr)
{
int err = gnutls_global_init();
@@ -85,27 +84,8 @@ CSecurityTLS::CSecurityTLS(CConnection* cc_, bool _anon)
void CSecurityTLS::shutdown()
{
- if (tlsos) {
- try {
- if (tlsos->hasBufferedData()) {
- tlsos->cork(false);
- tlsos->flush();
- if (tlsos->hasBufferedData())
- vlog.error("Failed to flush remaining socket data on close");
- }
- } catch (std::exception& e) {
- vlog.error("Failed to flush remaining socket data on close: %s", e.what());
- }
- }
-
- if (session) {
- int ret;
- // FIXME: We can't currently wait for the response, so we only send
- // our close and hope for the best
- ret = gnutls_bye(session, GNUTLS_SHUT_WR);
- if ((ret != GNUTLS_E_SUCCESS) && (ret != GNUTLS_E_INVALID_SESSION))
- vlog.error("TLS shutdown failed: %s", gnutls_strerror(ret));
- }
+ if (tlssock)
+ tlssock->shutdown();
if (anon_cred) {
gnutls_anon_free_client_credentials(anon_cred);
@@ -123,13 +103,9 @@ void CSecurityTLS::shutdown()
rawos = nullptr;
}
- if (tlsis) {
- delete tlsis;
- tlsis = nullptr;
- }
- if (tlsos) {
- delete tlsos;
- tlsos = nullptr;
+ if (tlssock) {
+ delete tlssock;
+ tlssock = nullptr;
}
if (session) {
@@ -171,26 +147,18 @@ bool CSecurityTLS::processMsg()
setParam();
- // Create these early as they set up the push/pull functions
- // for GnuTLS
- tlsis = new rdr::TLSInStream(is, session);
- tlsos = new rdr::TLSOutStream(os, session);
+ tlssock = new rdr::TLSSocket(is, os, session);
rawis = is;
rawos = os;
}
- int err;
- err = gnutls_handshake(session);
- if (err != GNUTLS_E_SUCCESS) {
- if (!gnutls_error_is_fatal(err)) {
- vlog.debug("Deferring completion of TLS handshake: %s", gnutls_strerror(err));
+ try {
+ if (!tlssock->handshake())
return false;
- }
-
- vlog.error("TLS Handshake failed: %s\n", gnutls_strerror (err));
+ } catch (std::exception&) {
shutdown();
- throw rdr::tls_error("TLS Handshake failed", err);
+ throw;
}
vlog.debug("TLS handshake completed with %s",
@@ -198,33 +166,29 @@ bool CSecurityTLS::processMsg()
checkSession();
- cc->setStreams(tlsis, tlsos);
+ cc->setStreams(&tlssock->inStream(), &tlssock->outStream());
return true;
}
void CSecurityTLS::setParam()
{
- static const char kx_anon_priority[] = ":+ANON-ECDH:+ANON-DH";
+ static const char kx_anon_priority[] = "+ANON-ECDH:+ANON-DH";
int ret;
// Custom priority string specified?
if (strcmp(Security::GnuTLSPriority, "") != 0) {
- char *prio;
+ std::string prio;
const char *err;
- prio = new char[strlen(Security::GnuTLSPriority) +
- strlen(kx_anon_priority) + 1];
-
- strcpy(prio, Security::GnuTLSPriority);
- if (anon)
- strcat(prio, kx_anon_priority);
-
- ret = gnutls_priority_set_direct(session, prio, &err);
-
- delete [] prio;
+ prio = (const char*)Security::GnuTLSPriority;
+ if (anon) {
+ prio += ":";
+ prio += kx_anon_priority;
+ }
+ ret = gnutls_priority_set_direct(session, prio.c_str(), &err);
if (ret != GNUTLS_E_SUCCESS) {
if (ret == GNUTLS_E_INVALID_REQUEST)
vlog.error("GnuTLS priority syntax error at: %s", err);
@@ -234,30 +198,22 @@ void CSecurityTLS::setParam()
const char *err;
#if GNUTLS_VERSION_NUMBER >= 0x030603
- // gnutls_set_default_priority_appends() expects a normal priority string that
- // doesn't start with ":".
- ret = gnutls_set_default_priority_append(session, kx_anon_priority + 1, &err, 0);
+ ret = gnutls_set_default_priority_append(session, kx_anon_priority, &err, 0);
if (ret != GNUTLS_E_SUCCESS) {
if (ret == GNUTLS_E_INVALID_REQUEST)
vlog.error("GnuTLS priority syntax error at: %s", err);
throw rdr::tls_error("gnutls_set_default_priority_append()", ret);
}
#else
+ std::string prio;
+
// We don't know what the system default priority is, so we guess
// it's what upstream GnuTLS has
- static const char gnutls_default_priority[] = "NORMAL";
- char *prio;
-
- prio = new char[malloc(strlen(gnutls_default_priority) +
- strlen(kx_anon_priority) + 1];
-
- strcpy(prio, gnutls_default_priority);
- strcat(prio, kx_anon_priority);
-
- ret = gnutls_priority_set_direct(session, prio, &err);
-
- delete [] prio;
+ prio = "NORMAL";
+ prio += ":";
+ prio += kx_anon_priority;
+ ret = gnutls_priority_set_direct(session, prio.c_str(), &err);
if (ret != GNUTLS_E_SUCCESS) {
if (ret == GNUTLS_E_INVALID_REQUEST)
vlog.error("GnuTLS priority syntax error at: %s", err);
@@ -277,6 +233,10 @@ void CSecurityTLS::setParam()
vlog.debug("Anonymous session has been set");
} else {
+ const char* hostname;
+ size_t len;
+ bool valid;
+
ret = gnutls_certificate_allocate_credentials(&cert_cred);
if (ret != GNUTLS_E_SUCCESS)
throw rdr::tls_error("gnutls_certificate_allocate_credentials()", ret);
@@ -294,10 +254,22 @@ void CSecurityTLS::setParam()
if (ret != GNUTLS_E_SUCCESS)
throw rdr::tls_error("gnutls_credentials_set()", ret);
- if (gnutls_server_name_set(session, GNUTLS_NAME_DNS,
- client->getServerName(),
- strlen(client->getServerName())) != GNUTLS_E_SUCCESS)
- vlog.error("Failed to configure the server name for TLS handshake");
+ // Only DNS hostnames are allowed, and some servers will reject the
+ // connection if we provide anything else (e.g. an IPv6 address)
+ hostname = client->getServerName();
+ len = strlen(hostname);
+ valid = true;
+ for (size_t i = 0; i < len; i++) {
+ if (!isalnum(hostname[i]) && hostname[i] != '.')
+ valid = false;
+ }
+
+ if (valid) {
+ if (gnutls_server_name_set(session, GNUTLS_NAME_DNS,
+ client->getServerName(),
+ strlen(client->getServerName())) != GNUTLS_E_SUCCESS)
+ vlog.error("Failed to configure the server name for TLS handshake");
+ }
vlog.debug("X509 session has been set");
}
@@ -324,12 +296,16 @@ void CSecurityTLS::checkSession()
if (anon)
return;
- if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509)
+ if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_UNSUPPORTED_CERTIFICATE);
throw protocol_error("Unsupported certificate type");
+ }
err = gnutls_certificate_verify_peers2(session, &status);
if (err != 0) {
vlog.error("Server certificate verification failed: %s", gnutls_strerror(err));
+ gnutls_alert_send_appropriate(session, err);
throw rdr::tls_error("Server certificate verification()", err);
}
@@ -346,13 +322,17 @@ void CSecurityTLS::checkSession()
GNUTLS_CRT_X509,
&status_str,
0);
- if (err != GNUTLS_E_SUCCESS)
+ if (err != GNUTLS_E_SUCCESS) {
+ gnutls_alert_send_appropriate(session, err);
throw rdr::tls_error("Failed to get certificate error description", err);
+ }
error = (const char*)status_str.data;
gnutls_free(status_str.data);
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw protocol_error(
core::format("Invalid server certificate: %s", error.c_str()));
}
@@ -361,8 +341,10 @@ void CSecurityTLS::checkSession()
GNUTLS_CRT_X509,
&status_str,
0);
- if (err != GNUTLS_E_SUCCESS)
+ if (err != GNUTLS_E_SUCCESS) {
+ gnutls_alert_send_appropriate(session, err);
throw rdr::tls_error("Failed to get certificate error description", err);
+ }
vlog.info("Server certificate errors: %s", status_str.data);
@@ -372,16 +354,21 @@ void CSecurityTLS::checkSession()
/* Process overridable errors later */
cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
- if (!cert_list_size)
+ if (!cert_list_size) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_UNSUPPORTED_CERTIFICATE);
throw protocol_error("Empty certificate chain");
+ }
/* Process only server's certificate, not issuer's certificate */
gnutls_x509_crt_t crt;
gnutls_x509_crt_init(&crt);
err = gnutls_x509_crt_import(crt, &cert_list[0], GNUTLS_X509_FMT_DER);
- if (err != GNUTLS_E_SUCCESS)
+ if (err != GNUTLS_E_SUCCESS) {
+ gnutls_alert_send_appropriate(session, err);
throw rdr::tls_error("Failed to decode server certificate", err);
+ }
if (gnutls_x509_crt_check_hostname(crt, client->getServerName()) == 0) {
vlog.info("Server certificate doesn't match given server name");
@@ -420,12 +407,15 @@ void CSecurityTLS::checkSession()
if ((known != GNUTLS_E_NO_CERTIFICATE_FOUND) &&
(known != GNUTLS_E_CERTIFICATE_KEY_MISMATCH)) {
+ gnutls_alert_send_appropriate(session, known);
throw rdr::tls_error("Could not load known hosts database", known);
}
err = gnutls_x509_crt_print(crt, GNUTLS_CRT_PRINT_ONELINE, &info);
- if (err != GNUTLS_E_SUCCESS)
+ if (err != GNUTLS_E_SUCCESS) {
+ gnutls_alert_send_appropriate(session, known);
throw rdr::tls_error("Could not find certificate to display", err);
+ }
len = strlen((char*)info.data);
for (size_t i = 0; i < len - 1; i++) {
@@ -456,8 +446,11 @@ void CSecurityTLS::checkSession()
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Unknown certificate issuer",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_UNKNOWN_CA);
throw auth_cancelled();
+ }
status &= ~(GNUTLS_CERT_INVALID |
GNUTLS_CERT_SIGNER_NOT_FOUND |
@@ -478,8 +471,11 @@ void CSecurityTLS::checkSession()
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Certificate is not yet valid",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw auth_cancelled();
+ }
status &= ~GNUTLS_CERT_NOT_ACTIVATED;
}
@@ -498,8 +494,11 @@ void CSecurityTLS::checkSession()
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Expired certificate",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw auth_cancelled();
+ }
status &= ~GNUTLS_CERT_EXPIRED;
}
@@ -518,14 +517,19 @@ void CSecurityTLS::checkSession()
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Insecure certificate algorithm",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw auth_cancelled();
+ }
status &= ~GNUTLS_CERT_INSECURE_ALGORITHM;
}
if (status != 0) {
vlog.error("Unhandled certificate problems: 0x%x", status);
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw std::logic_error("Unhandled certificate problems");
}
@@ -544,8 +548,11 @@ void CSecurityTLS::checkSession()
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Certificate hostname mismatch",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw auth_cancelled();
+ }
}
} else if (known == GNUTLS_E_CERTIFICATE_KEY_MISMATCH) {
std::string text;
@@ -571,8 +578,11 @@ void CSecurityTLS::checkSession()
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Unexpected server certificate",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_UNKNOWN_CA);
throw auth_cancelled();
+ }
status &= ~(GNUTLS_CERT_INVALID |
GNUTLS_CERT_SIGNER_NOT_FOUND |
@@ -594,8 +604,11 @@ void CSecurityTLS::checkSession()
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Unexpected server certificate",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw auth_cancelled();
+ }
status &= ~GNUTLS_CERT_NOT_ACTIVATED;
}
@@ -615,8 +628,11 @@ void CSecurityTLS::checkSession()
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Unexpected server certificate",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw auth_cancelled();
+ }
status &= ~GNUTLS_CERT_EXPIRED;
}
@@ -636,14 +652,19 @@ void CSecurityTLS::checkSession()
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Unexpected server certificate",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw auth_cancelled();
+ }
status &= ~GNUTLS_CERT_INSECURE_ALGORITHM;
}
if (status != 0) {
vlog.error("Unhandled certificate problems: 0x%x", status);
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw std::logic_error("Unhandled certificate problems");
}
@@ -663,8 +684,11 @@ void CSecurityTLS::checkSession()
if (!cc->showMsgBox(MsgBoxFlags::M_YESNO,
"Unexpected server certificate",
- text.c_str()))
+ text.c_str())) {
+ gnutls_alert_send(session, GNUTLS_AL_FATAL,
+ GNUTLS_A_BAD_CERTIFICATE);
throw auth_cancelled();
+ }
}
}
diff --git a/common/rfb/CSecurityTLS.h b/common/rfb/CSecurityTLS.h
index 9b70366d..51b7dac1 100644
--- a/common/rfb/CSecurityTLS.h
+++ b/common/rfb/CSecurityTLS.h
@@ -2,6 +2,7 @@
* Copyright (C) 2004 Red Hat Inc.
* Copyright (C) 2005 Martin Koegler
* Copyright (C) 2010 TigerVNC Team
+ * Copyright 2012-2025 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -34,8 +35,7 @@
namespace rdr {
class InStream;
class OutStream;
- class TLSInStream;
- class TLSOutStream;
+ class TLSSocket;
}
namespace rfb {
@@ -63,8 +63,7 @@ namespace rfb {
gnutls_certificate_credentials_t cert_cred;
bool anon;
- rdr::TLSInStream* tlsis;
- rdr::TLSOutStream* tlsos;
+ rdr::TLSSocket* tlssock;
rdr::InStream* rawis;
rdr::OutStream* rawos;
diff --git a/common/rfb/ClientParams.cxx b/common/rfb/ClientParams.cxx
index 972b89e8..514b0b4e 100644
--- a/common/rfb/ClientParams.cxx
+++ b/common/rfb/ClientParams.cxx
@@ -24,6 +24,7 @@
#include <stdexcept>
+#include <core/LogWriter.h>
#include <core/string.h>
#include <rfb/encodings.h>
@@ -35,6 +36,8 @@
using namespace rfb;
+static core::LogWriter vlog("ClientParams");
+
ClientParams::ClientParams()
: majorVersion(0), minorVersion(0),
compressLevel(2), qualityLevel(-1), fineQualityLevel(-1),
@@ -72,8 +75,14 @@ void ClientParams::setDimensions(int width, int height)
void ClientParams::setDimensions(int width, int height, const ScreenSet& layout)
{
- if (!layout.validate(width, height))
+ if (!layout.validate(width, height)) {
+ char buffer[2048];
+ vlog.debug("Invalid screen layout for %dx%d:", width, height);
+ layout.print(buffer, sizeof(buffer));
+ vlog.debug("%s", buffer);
+
throw std::invalid_argument("Attempted to configure an invalid screen layout");
+ }
width_ = width;
height_ = height;
@@ -247,4 +256,4 @@ bool ClientParams::supportsExtendedMouseButtons() const
if (supportsEncoding(pseudoEncodingExtendedMouseButtons))
return true;
return false;
-} \ No newline at end of file
+}
diff --git a/common/rfb/DecodeManager.cxx b/common/rfb/DecodeManager.cxx
index 94908f86..48181f94 100644
--- a/common/rfb/DecodeManager.cxx
+++ b/common/rfb/DecodeManager.cxx
@@ -24,7 +24,6 @@
#include <string.h>
#include <core/LogWriter.h>
-#include <core/Mutex.h>
#include <core/Region.h>
#include <core/string.h>
@@ -48,11 +47,7 @@ DecodeManager::DecodeManager(CConnection *conn_) :
memset(stats, 0, sizeof(stats));
- queueMutex = new core::Mutex();
- producerCond = new core::Condition(queueMutex);
- consumerCond = new core::Condition(queueMutex);
-
- cpuCount = core::Thread::getSystemCPUCount();
+ cpuCount = std::thread::hardware_concurrency();
if (cpuCount == 0) {
vlog.error("Unable to determine the number of CPU cores on this system");
cpuCount = 1;
@@ -90,10 +85,6 @@ DecodeManager::~DecodeManager()
freeBuffers.pop_back();
}
- delete consumerCond;
- delete producerCond;
- delete queueMutex;
-
for (Decoder* decoder : decoders)
delete decoder;
}
@@ -125,29 +116,25 @@ bool DecodeManager::decodeRect(const core::Rect& r, int encoding,
decoder = decoders[encoding];
// Wait for an available memory buffer
- queueMutex->lock();
+ std::unique_lock<std::mutex> lock(queueMutex);
// FIXME: Should we return and let other things run here?
while (freeBuffers.empty())
- producerCond->wait();
+ producerCond.wait(lock);
// Don't pop the buffer in case we throw an exception
// whilst reading
bufferStream = freeBuffers.front();
- queueMutex->unlock();
+ lock.unlock();
// First check if any thread has encountered a problem
throwThreadException();
// Read the rect
bufferStream->clear();
- try {
- if (!decoder->readRect(r, conn->getInStream(), conn->server, bufferStream))
- return false;
- } catch (std::exception& e) {
- throw std::runtime_error(core::format("Error reading rect: %s", e.what()));
- }
+ if (!decoder->readRect(r, conn->getInStream(), conn->server, bufferStream))
+ return false;
stats[encoding].rects++;
stats[encoding].bytes += 12 + bufferStream->length();
@@ -170,7 +157,7 @@ bool DecodeManager::decodeRect(const core::Rect& r, int encoding,
bufferStream->length(), conn->server,
&entry->affectedRegion);
- queueMutex->lock();
+ lock.lock();
// The workers add buffers to the end so it's safe to assume
// the front is still the same buffer
@@ -180,21 +167,21 @@ bool DecodeManager::decodeRect(const core::Rect& r, int encoding,
// We only put a single entry on the queue so waking a single
// thread is sufficient
- consumerCond->signal();
+ consumerCond.notify_one();
- queueMutex->unlock();
+ lock.unlock();
return true;
}
void DecodeManager::flush()
{
- queueMutex->lock();
+ std::unique_lock<std::mutex> lock(queueMutex);
while (!workQueue.empty())
- producerCond->wait();
+ producerCond.wait(lock);
- queueMutex->unlock();
+ lock.unlock();
throwThreadException();
}
@@ -242,7 +229,7 @@ void DecodeManager::logStats()
void DecodeManager::setThreadException()
{
- core::AutoMutex a(queueMutex);
+ const std::lock_guard<std::mutex> lock(queueMutex);
if (threadException)
return;
@@ -252,7 +239,7 @@ void DecodeManager::setThreadException()
void DecodeManager::throwThreadException()
{
- core::AutoMutex a(queueMutex);
+ const std::lock_guard<std::mutex> lock(queueMutex);
if (!threadException)
return;
@@ -266,7 +253,7 @@ void DecodeManager::throwThreadException()
}
DecodeManager::DecodeThread::DecodeThread(DecodeManager* manager_)
- : manager(manager_), stopRequested(false)
+ : manager(manager_), thread(nullptr), stopRequested(false)
{
start();
}
@@ -274,25 +261,35 @@ DecodeManager::DecodeThread::DecodeThread(DecodeManager* manager_)
DecodeManager::DecodeThread::~DecodeThread()
{
stop();
- wait();
+ if (thread != nullptr) {
+ thread->join();
+ delete thread;
+ }
+}
+
+void DecodeManager::DecodeThread::start()
+{
+ assert(thread == nullptr);
+
+ thread = new std::thread(&DecodeThread::worker, this);
}
void DecodeManager::DecodeThread::stop()
{
- core::AutoMutex a(manager->queueMutex);
+ const std::lock_guard<std::mutex> lock(manager->queueMutex);
- if (!isRunning())
+ if (thread == nullptr)
return;
stopRequested = true;
// We can't wake just this thread, so wake everyone
- manager->consumerCond->broadcast();
+ manager->consumerCond.notify_all();
}
void DecodeManager::DecodeThread::worker()
{
- manager->queueMutex->lock();
+ std::unique_lock<std::mutex> lock(manager->queueMutex);
while (!stopRequested) {
DecodeManager::QueueEntry *entry;
@@ -301,14 +298,14 @@ void DecodeManager::DecodeThread::worker()
entry = findEntry();
if (entry == nullptr) {
// Wait and try again
- manager->consumerCond->wait();
+ manager->consumerCond.wait(lock);
continue;
}
// This is ours now
entry->active = true;
- manager->queueMutex->unlock();
+ lock.unlock();
// Do the actual decoding
try {
@@ -321,7 +318,7 @@ void DecodeManager::DecodeThread::worker()
assert(false);
}
- manager->queueMutex->lock();
+ lock.lock();
// Remove the entry from the queue and give back the memory buffer
manager->freeBuffers.push_back(entry->bufferStream);
@@ -329,14 +326,12 @@ void DecodeManager::DecodeThread::worker()
delete entry;
// Wake the main thread in case it is waiting for a memory buffer
- manager->producerCond->signal();
+ manager->producerCond.notify_one();
// This rect might have been blocking multiple other rects, so
// wake up every worker thread
if (manager->workQueue.size() > 1)
- manager->consumerCond->broadcast();
+ manager->consumerCond.notify_all();
}
-
- manager->queueMutex->unlock();
}
DecodeManager::QueueEntry* DecodeManager::DecodeThread::findEntry()
diff --git a/common/rfb/DecodeManager.h b/common/rfb/DecodeManager.h
index 95d3ceca..146bf8ae 100644
--- a/common/rfb/DecodeManager.h
+++ b/common/rfb/DecodeManager.h
@@ -19,17 +19,17 @@
#ifndef __RFB_DECODEMANAGER_H__
#define __RFB_DECODEMANAGER_H__
+#include <condition_variable>
#include <exception>
#include <list>
+#include <mutex>
+#include <thread>
#include <core/Region.h>
-#include <core/Thread.h>
#include <rfb/encodings.h>
namespace core {
- class Condition;
- class Mutex;
struct Rect;
}
@@ -86,25 +86,27 @@ namespace rfb {
std::list<rdr::MemOutStream*> freeBuffers;
std::list<QueueEntry*> workQueue;
- core::Mutex* queueMutex;
- core::Condition* producerCond;
- core::Condition* consumerCond;
+ std::mutex queueMutex;
+ std::condition_variable producerCond;
+ std::condition_variable consumerCond;
private:
- class DecodeThread : public core::Thread {
+ class DecodeThread {
public:
DecodeThread(DecodeManager* manager);
~DecodeThread();
+ void start();
void stop();
protected:
- void worker() override;
+ void worker();
DecodeManager::QueueEntry* findEntry();
private:
DecodeManager* manager;
+ std::thread* thread;
bool stopRequested;
};
diff --git a/common/rfb/EncodeManager.cxx b/common/rfb/EncodeManager.cxx
index 7ec70e69..6a63fa6f 100644
--- a/common/rfb/EncodeManager.cxx
+++ b/common/rfb/EncodeManager.cxx
@@ -278,6 +278,13 @@ void EncodeManager::pruneLosslessRefresh(const core::Region& limits)
pendingRefreshRegion.assign_intersect(limits);
}
+void EncodeManager::forceRefresh(const core::Region& req)
+{
+ lossyRegion.assign_union(req);
+ if (!recentChangeTimer.isStarted())
+ pendingRefreshRegion.assign_union(req);
+}
+
void EncodeManager::writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb,
const RenderedCursor* renderedCursor)
{
diff --git a/common/rfb/EncodeManager.h b/common/rfb/EncodeManager.h
index 959c13d6..4ce6d0ce 100644
--- a/common/rfb/EncodeManager.h
+++ b/common/rfb/EncodeManager.h
@@ -54,6 +54,8 @@ namespace rfb {
void pruneLosslessRefresh(const core::Region& limits);
+ void forceRefresh(const core::Region& req);
+
void writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb,
const RenderedCursor* renderedCursor);
diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx
index c269fac0..c698b991 100644
--- a/common/rfb/SConnection.cxx
+++ b/common/rfb/SConnection.cxx
@@ -154,8 +154,6 @@ bool SConnection::processVersionMsg()
client.majorVersion,client.minorVersion);
}
- versionReceived();
-
std::list<uint8_t> secTypes;
std::list<uint8_t>::iterator i;
secTypes = security.GetEnabledSecTypes();
@@ -346,6 +344,8 @@ bool SConnection::accessCheck(AccessRights ar) const
void SConnection::setEncodings(int nEncodings, const int32_t* encodings)
{
int i;
+ bool firstFence, firstContinuousUpdates, firstLEDState,
+ firstQEMUKeyEvent, firstExtMouseButtonsEvent;
preferredEncoding = encodingRaw;
for (i = 0;i < nEncodings;i++) {
@@ -355,7 +355,26 @@ void SConnection::setEncodings(int nEncodings, const int32_t* encodings)
}
}
- SMsgHandler::setEncodings(nEncodings, encodings);
+ firstFence = !client.supportsFence();
+ firstContinuousUpdates = !client.supportsContinuousUpdates();
+ firstLEDState = !client.supportsLEDState();
+ firstQEMUKeyEvent = !client.supportsEncoding(pseudoEncodingQEMUKeyEvent);
+ firstExtMouseButtonsEvent = !client.supportsEncoding(pseudoEncodingExtendedMouseButtons);
+
+ client.setEncodings(nEncodings, encodings);
+
+ supportsLocalCursor();
+
+ if (client.supportsFence() && firstFence)
+ supportsFence();
+ if (client.supportsContinuousUpdates() && firstContinuousUpdates)
+ supportsContinuousUpdates();
+ if (client.supportsLEDState() && firstLEDState)
+ supportsLEDState();
+ if (client.supportsEncoding(pseudoEncodingQEMUKeyEvent) && firstQEMUKeyEvent)
+ writer()->writeQEMUKeyEvent();
+ if (client.supportsEncoding(pseudoEncodingExtendedMouseButtons) && firstExtMouseButtonsEvent)
+ writer()->writeExtendedMouseButtonsSupport();
if (client.supportsEncoding(pseudoEncodingExtendedClipboard)) {
uint32_t sizes[] = { 0 };
@@ -375,9 +394,54 @@ void SConnection::clientCutText(const char* str)
clientClipboard = str;
hasRemoteClipboard = true;
+ if (!accessCheck(AccessCutText))
+ return;
+
handleClipboardAnnounce(true);
}
+void SConnection::handleClipboardCaps(uint32_t flags, const uint32_t* lengths)
+{
+ int i;
+
+ vlog.debug("Got client clipboard capabilities:");
+ for (i = 0;i < 16;i++) {
+ if (flags & (1 << i)) {
+ const char *type;
+
+ switch (1 << i) {
+ case clipboardUTF8:
+ type = "Plain text";
+ break;
+ case clipboardRTF:
+ type = "Rich text";
+ break;
+ case clipboardHTML:
+ type = "HTML";
+ break;
+ case clipboardDIB:
+ type = "Images";
+ break;
+ case clipboardFiles:
+ type = "Files";
+ break;
+ default:
+ vlog.debug(" Unknown format 0x%x", 1 << i);
+ continue;
+ }
+
+ if (lengths[i] == 0)
+ vlog.debug(" %s (only notify)", type);
+ else {
+ vlog.debug(" %s (automatically send up to %s)",
+ type, core::iecPrefix(lengths[i], "B").c_str());
+ }
+ }
+ }
+
+ client.setClipboardCaps(flags, lengths);
+}
+
void SConnection::handleClipboardRequest(uint32_t flags)
{
if (!(flags & rfb::clipboardUTF8)) {
@@ -388,6 +452,8 @@ void SConnection::handleClipboardRequest(uint32_t flags)
vlog.debug("Ignoring unexpected clipboard request");
return;
}
+ if (!accessCheck(AccessCutText))
+ return;
handleClipboardRequest();
}
@@ -403,10 +469,15 @@ void SConnection::handleClipboardNotify(uint32_t flags)
if (flags & rfb::clipboardUTF8) {
hasLocalClipboard = false;
+ if (!accessCheck(AccessCutText))
+ return;
handleClipboardAnnounce(true);
} else {
+ if (!accessCheck(AccessCutText))
+ return;
handleClipboardAnnounce(false);
}
+
}
void SConnection::handleClipboardProvide(uint32_t flags,
@@ -426,21 +497,26 @@ void SConnection::handleClipboardProvide(uint32_t flags,
clientClipboard = core::convertLF((const char*)data[0], lengths[0]);
hasRemoteClipboard = true;
+ if (!accessCheck(AccessCutText))
+ return;
+
// FIXME: Should probably verify that this data was actually requested
handleClipboardData(clientClipboard.c_str());
}
-void SConnection::supportsQEMUKeyEvent()
+void SConnection::supportsLocalCursor()
+{
+}
+
+void SConnection::supportsFence()
{
- writer()->writeQEMUKeyEvent();
}
-void SConnection::supportsExtendedMouseButtons()
+void SConnection::supportsContinuousUpdates()
{
- writer()->writeExtendedMouseButtonsSupport();
}
-void SConnection::versionReceived()
+void SConnection::supportsLEDState()
{
}
@@ -502,7 +578,7 @@ void SConnection::close(const char* /*reason*/)
void SConnection::setPixelFormat(const PixelFormat& pf)
{
- SMsgHandler::setPixelFormat(pf);
+ client.setPF(pf);
readyForSetColourMapEntries = true;
if (!pf.trueColour)
writeFakeColourMap();
@@ -551,6 +627,9 @@ void SConnection::handleClipboardData(const char* /*data*/)
void SConnection::requestClipboard()
{
+ if (!accessCheck(AccessCutText))
+ return;
+
if (hasRemoteClipboard) {
handleClipboardData(clientClipboard.c_str());
return;
@@ -563,6 +642,9 @@ void SConnection::requestClipboard()
void SConnection::announceClipboard(bool available)
{
+ if (!accessCheck(AccessCutText))
+ return;
+
hasLocalClipboard = available;
unsolicitedClipboardAttempt = false;
@@ -589,6 +671,9 @@ void SConnection::announceClipboard(bool available)
void SConnection::sendClipboardData(const char* data)
{
+ if (!accessCheck(AccessCutText))
+ return;
+
if (client.supportsEncoding(pseudoEncodingExtendedClipboard) &&
(client.clipboardFlags() & rfb::clipboardProvide)) {
// FIXME: This conversion magic should be in SMsgWriter
diff --git a/common/rfb/SConnection.h b/common/rfb/SConnection.h
index 0f4de5a5..a90b37ca 100644
--- a/common/rfb/SConnection.h
+++ b/common/rfb/SConnection.h
@@ -87,6 +87,59 @@ namespace rfb {
// cleanup of the SConnection object by the server
virtual void close(const char* reason);
+ // requestClipboard() will result in a request to the client to
+ // transfer its clipboard data. A call to handleClipboardData()
+ // will be made once the data is available.
+ virtual void requestClipboard();
+
+ // announceClipboard() informs the client of changes to the
+ // clipboard on the server. The client may later request the
+ // clipboard data via handleClipboardRequest().
+ virtual void announceClipboard(bool available);
+
+ // sendClipboardData() transfers the clipboard data to the client
+ // and should be called whenever the client has requested the
+ // clipboard via handleClipboardRequest().
+ virtual void sendClipboardData(const char* data);
+
+ // getAccessRights() returns the access rights of a SConnection to the server.
+ AccessRights getAccessRights() { return accessRights; }
+
+ // setAccessRights() allows a security package to limit the access rights
+ // of a SConnection to the server. How the access rights are treated
+ // is up to the derived class.
+ virtual void setAccessRights(AccessRights ar);
+ virtual bool accessCheck(AccessRights ar) const;
+
+ // authenticated() returns true if the client has authenticated
+ // successfully.
+ bool authenticated() { return (state_ == RFBSTATE_INITIALISATION ||
+ state_ == RFBSTATE_NORMAL); }
+
+ SMsgReader* reader() { return reader_; }
+ SMsgWriter* writer() { return writer_; }
+
+ rdr::InStream* getInStream() { return is; }
+ rdr::OutStream* getOutStream() { return os; }
+
+ enum stateEnum {
+ RFBSTATE_UNINITIALISED,
+ RFBSTATE_PROTOCOL_VERSION,
+ RFBSTATE_SECURITY_TYPE,
+ RFBSTATE_SECURITY,
+ RFBSTATE_SECURITY_FAILURE,
+ RFBSTATE_QUERYING,
+ RFBSTATE_INITIALISATION,
+ RFBSTATE_NORMAL,
+ RFBSTATE_CLOSING,
+ RFBSTATE_INVALID
+ };
+
+ stateEnum state() { return state_; }
+
+ int32_t getPreferredEncoding() { return preferredEncoding; }
+
+ protected:
// Overridden from SMsgHandler
@@ -94,23 +147,38 @@ namespace rfb {
void clientCutText(const char* str) override;
+ void handleClipboardCaps(uint32_t flags,
+ const uint32_t* lengths) override;
void handleClipboardRequest(uint32_t flags) override;
void handleClipboardPeek() override;
void handleClipboardNotify(uint32_t flags) override;
void handleClipboardProvide(uint32_t flags, const size_t* lengths,
const uint8_t* const* data) override;
- void supportsQEMUKeyEvent() override;
-
- virtual void supportsExtendedMouseButtons() override;
-
-
// Methods to be overridden in a derived class
- // versionReceived() indicates that the version number has just been read
- // from the client. The version will already have been "cooked"
- // to deal with unknown/bogus viewer protocol numbers.
- virtual void versionReceived();
+ // supportsLocalCursor() is called whenever the status of
+ // cp.supportsLocalCursor has changed. At the moment this happens on a
+ // setEncodings message, but in the future this may be due to a message
+ // specially for this purpose.
+ virtual void supportsLocalCursor();
+
+ // supportsFence() is called the first time we detect support for fences
+ // in the client. A fence message should be sent at this point to notify
+ // the client of server support.
+ virtual void supportsFence();
+
+ // supportsContinuousUpdates() is called the first time we detect that
+ // the client wants the continuous updates extension. A
+ // EndOfContinuousUpdates message should be sent back to the client at
+ // this point if it is supported.
+ virtual void supportsContinuousUpdates();
+
+ // supportsLEDState() is called the first time we detect that the
+ // client supports the LED state extension. A LEDState message
+ // should be sent back to the client to inform it of the current
+ // server state.
+ virtual void supportsLEDState();
// authSuccess() is called when authentication has succeeded.
virtual void authSuccess();
@@ -166,62 +234,6 @@ namespace rfb {
// client received the request.
virtual void handleClipboardData(const char* data);
-
- // Other methods
-
- // requestClipboard() will result in a request to the client to
- // transfer its clipboard data. A call to handleClipboardData()
- // will be made once the data is available.
- virtual void requestClipboard();
-
- // announceClipboard() informs the client of changes to the
- // clipboard on the server. The client may later request the
- // clipboard data via handleClipboardRequest().
- virtual void announceClipboard(bool available);
-
- // sendClipboardData() transfers the clipboard data to the client
- // and should be called whenever the client has requested the
- // clipboard via handleClipboardRequest().
- virtual void sendClipboardData(const char* data);
-
- // getAccessRights() returns the access rights of a SConnection to the server.
- AccessRights getAccessRights() { return accessRights; }
-
- // setAccessRights() allows a security package to limit the access rights
- // of a SConnection to the server. How the access rights are treated
- // is up to the derived class.
- virtual void setAccessRights(AccessRights ar);
- virtual bool accessCheck(AccessRights ar) const;
-
- // authenticated() returns true if the client has authenticated
- // successfully.
- bool authenticated() { return (state_ == RFBSTATE_INITIALISATION ||
- state_ == RFBSTATE_NORMAL); }
-
- SMsgReader* reader() { return reader_; }
- SMsgWriter* writer() { return writer_; }
-
- rdr::InStream* getInStream() { return is; }
- rdr::OutStream* getOutStream() { return os; }
-
- enum stateEnum {
- RFBSTATE_UNINITIALISED,
- RFBSTATE_PROTOCOL_VERSION,
- RFBSTATE_SECURITY_TYPE,
- RFBSTATE_SECURITY,
- RFBSTATE_SECURITY_FAILURE,
- RFBSTATE_QUERYING,
- RFBSTATE_INITIALISATION,
- RFBSTATE_NORMAL,
- RFBSTATE_CLOSING,
- RFBSTATE_INVALID
- };
-
- stateEnum state() { return state_; }
-
- int32_t getPreferredEncoding() { return preferredEncoding; }
-
- protected:
// failConnection() prints a message to the log, sends a connection
// failed message to the client (if possible) and throws an
// Exception.
diff --git a/common/rfb/SMsgHandler.cxx b/common/rfb/SMsgHandler.cxx
deleted file mode 100644
index 9eb5ae08..00000000
--- a/common/rfb/SMsgHandler.cxx
+++ /dev/null
@@ -1,176 +0,0 @@
-/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2019 Pierre Ossman for Cendio AB
- *
- * 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.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <core/LogWriter.h>
-#include <core/string.h>
-
-#include <rfb/SMsgHandler.h>
-#include <rfb/ScreenSet.h>
-#include <rfb/clipboardTypes.h>
-#include <rfb/encodings.h>
-
-using namespace rfb;
-
-static core::LogWriter vlog("SMsgHandler");
-
-SMsgHandler::SMsgHandler()
-{
-}
-
-SMsgHandler::~SMsgHandler()
-{
-}
-
-void SMsgHandler::clientInit(bool /*shared*/)
-{
-}
-
-void SMsgHandler::setPixelFormat(const PixelFormat& pf)
-{
- client.setPF(pf);
-}
-
-void SMsgHandler::setEncodings(int nEncodings, const int32_t* encodings)
-{
- bool firstFence, firstContinuousUpdates, firstLEDState,
- firstQEMUKeyEvent, firstExtMouseButtonsEvent;
-
- firstFence = !client.supportsFence();
- firstContinuousUpdates = !client.supportsContinuousUpdates();
- firstLEDState = !client.supportsLEDState();
- firstQEMUKeyEvent = !client.supportsEncoding(pseudoEncodingQEMUKeyEvent);
- firstExtMouseButtonsEvent = !client.supportsEncoding(pseudoEncodingExtendedMouseButtons);
-
- client.setEncodings(nEncodings, encodings);
-
- supportsLocalCursor();
-
- if (client.supportsFence() && firstFence)
- supportsFence();
- if (client.supportsContinuousUpdates() && firstContinuousUpdates)
- supportsContinuousUpdates();
- if (client.supportsLEDState() && firstLEDState)
- supportsLEDState();
- if (client.supportsEncoding(pseudoEncodingQEMUKeyEvent) && firstQEMUKeyEvent)
- supportsQEMUKeyEvent();
- if (client.supportsEncoding(pseudoEncodingExtendedMouseButtons) && firstExtMouseButtonsEvent)
- supportsExtendedMouseButtons();
-}
-
-void SMsgHandler::keyEvent(uint32_t /*keysym*/, uint32_t /*keycode*/,
- bool /*down*/)
-{
-}
-
-void SMsgHandler::pointerEvent(const core::Point& /*pos*/,
- uint16_t /*buttonMask*/)
-{
-}
-
-void SMsgHandler::clientCutText(const char* /*str*/)
-{
-}
-
-void SMsgHandler::handleClipboardCaps(uint32_t flags, const uint32_t* lengths)
-{
- int i;
-
- vlog.debug("Got client clipboard capabilities:");
- for (i = 0;i < 16;i++) {
- if (flags & (1 << i)) {
- const char *type;
-
- switch (1 << i) {
- case clipboardUTF8:
- type = "Plain text";
- break;
- case clipboardRTF:
- type = "Rich text";
- break;
- case clipboardHTML:
- type = "HTML";
- break;
- case clipboardDIB:
- type = "Images";
- break;
- case clipboardFiles:
- type = "Files";
- break;
- default:
- vlog.debug(" Unknown format 0x%x", 1 << i);
- continue;
- }
-
- if (lengths[i] == 0)
- vlog.debug(" %s (only notify)", type);
- else {
- vlog.debug(" %s (automatically send up to %s)",
- type, core::iecPrefix(lengths[i], "B").c_str());
- }
- }
- }
-
- client.setClipboardCaps(flags, lengths);
-}
-
-void SMsgHandler::handleClipboardRequest(uint32_t /*flags*/)
-{
-}
-
-void SMsgHandler::handleClipboardPeek()
-{
-}
-
-void SMsgHandler::handleClipboardNotify(uint32_t /*flags*/)
-{
-}
-
-void SMsgHandler::handleClipboardProvide(uint32_t /*flags*/,
- const size_t* /*lengths*/,
- const uint8_t* const* /*data*/)
-{
-}
-
-void SMsgHandler::supportsLocalCursor()
-{
-}
-
-void SMsgHandler::supportsFence()
-{
-}
-
-void SMsgHandler::supportsContinuousUpdates()
-{
-}
-
-void SMsgHandler::supportsLEDState()
-{
-}
-
-void SMsgHandler::supportsQEMUKeyEvent()
-{
-}
-
-void SMsgHandler::supportsExtendedMouseButtons()
-{
-} \ No newline at end of file
diff --git a/common/rfb/SMsgHandler.h b/common/rfb/SMsgHandler.h
index f5f5b769..d14a21c0 100644
--- a/common/rfb/SMsgHandler.h
+++ b/common/rfb/SMsgHandler.h
@@ -27,80 +27,45 @@
#include <rfb/ClientParams.h>
-namespace rdr { class InStream; }
-
namespace rfb {
class SMsgHandler {
public:
- SMsgHandler();
- virtual ~SMsgHandler();
+ virtual ~SMsgHandler() {}
- // The following methods are called as corresponding messages are read. A
- // derived class should override these methods as desired. Note that for
- // the setPixelFormat(), setEncodings() and clipboardCaps() methods, a
- // derived class must call on to SMsgHandler's methods.
+ // The following methods are called as corresponding messages are
+ // read. A derived class must override these methods.
- virtual void clientInit(bool shared);
+ virtual void clientInit(bool shared) = 0;
- virtual void setPixelFormat(const PixelFormat& pf);
- virtual void setEncodings(int nEncodings, const int32_t* encodings);
+ virtual void setPixelFormat(const PixelFormat& pf) = 0;
+ virtual void setEncodings(int nEncodings,
+ const int32_t* encodings) = 0;
virtual void framebufferUpdateRequest(const core::Rect& r,
bool incremental) = 0;
virtual void setDesktopSize(int fb_width, int fb_height,
const ScreenSet& layout) = 0;
- virtual void fence(uint32_t flags, unsigned len, const uint8_t data[]) = 0;
+ virtual void fence(uint32_t flags, unsigned len,
+ const uint8_t data[]) = 0;
virtual void enableContinuousUpdates(bool enable,
- int x, int y, int w, int h) = 0;
+ int x, int y,
+ int w, int h) = 0;
virtual void keyEvent(uint32_t keysym, uint32_t keycode,
- bool down);
+ bool down) = 0;
virtual void pointerEvent(const core::Point& pos,
- uint16_t buttonMask);
+ uint16_t buttonMask) = 0;
- virtual void clientCutText(const char* str);
+ virtual void clientCutText(const char* str) = 0;
virtual void handleClipboardCaps(uint32_t flags,
- const uint32_t* lengths);
- virtual void handleClipboardRequest(uint32_t flags);
- virtual void handleClipboardPeek();
- virtual void handleClipboardNotify(uint32_t flags);
+ const uint32_t* lengths) = 0;
+ virtual void handleClipboardRequest(uint32_t flags) = 0;
+ virtual void handleClipboardPeek() = 0;
+ virtual void handleClipboardNotify(uint32_t flags) = 0;
virtual void handleClipboardProvide(uint32_t flags,
const size_t* lengths,
- const uint8_t* const* data);
-
- // supportsLocalCursor() is called whenever the status of
- // cp.supportsLocalCursor has changed. At the moment this happens on a
- // setEncodings message, but in the future this may be due to a message
- // specially for this purpose.
- virtual void supportsLocalCursor();
-
- // supportsFence() is called the first time we detect support for fences
- // in the client. A fence message should be sent at this point to notify
- // the client of server support.
- virtual void supportsFence();
-
- // supportsContinuousUpdates() is called the first time we detect that
- // the client wants the continuous updates extension. A
- // EndOfContinuousUpdates message should be sent back to the client at
- // this point if it is supported.
- virtual void supportsContinuousUpdates();
-
- // supportsLEDState() is called the first time we detect that the
- // client supports the LED state extension. A LEDState message
- // should be sent back to the client to inform it of the current
- // server state.
- virtual void supportsLEDState();
-
- // supportsQEMUKeyEvent() is called the first time we detect that the
- // client wants the QEMU Extended Key Event extension. The default
- // handler will send a pseudo-rect back, signalling server support.
- virtual void supportsQEMUKeyEvent();
-
- // supportsExtendedMouseButtons() is called the first time we detect that the
- // client supports sending 16 bit mouse button state. This lets us pass more button
- // states between server and client.
- virtual void supportsExtendedMouseButtons();
+ const uint8_t* const* data) = 0;
ClientParams client;
};
diff --git a/common/rfb/SSecurityPlain.cxx b/common/rfb/SSecurityPlain.cxx
index 06631f81..4fa63250 100644
--- a/common/rfb/SSecurityPlain.cxx
+++ b/common/rfb/SSecurityPlain.cxx
@@ -113,8 +113,9 @@ bool SSecurityPlain::processMsg()
password[plen] = 0;
username[ulen] = 0;
plen = 0;
- if (!valid->validate(sc, username, password))
- throw auth_error("Authentication failed");
+ std::string msg = "Authentication failed";
+ if (!valid->validate(sc, username, password, msg))
+ throw auth_error(msg);
}
return true;
diff --git a/common/rfb/SSecurityPlain.h b/common/rfb/SSecurityPlain.h
index f2bc3483..4c030455 100644
--- a/common/rfb/SSecurityPlain.h
+++ b/common/rfb/SSecurityPlain.h
@@ -29,14 +29,20 @@ namespace rfb {
class PasswordValidator {
public:
- bool validate(SConnection* sc, const char *username, const char *password)
- { return validUser(username) ? validateInternal(sc, username, password) : false; }
+ bool validate(SConnection* sc,
+ const char *username,
+ const char *password,
+ std::string &msg)
+ { return validUser(username) ? validateInternal(sc, username, password, msg) : false; }
static core::StringListParameter plainUsers;
virtual ~PasswordValidator() { }
protected:
- virtual bool validateInternal(SConnection* sc, const char *username, const char *password)=0;
+ virtual bool validateInternal(SConnection* sc,
+ const char *username,
+ const char *password,
+ std::string &msg) = 0;
static bool validUser(const char* username);
};
diff --git a/common/rfb/SSecurityRSAAES.cxx b/common/rfb/SSecurityRSAAES.cxx
index 6afb52dd..405005ab 100644
--- a/common/rfb/SSecurityRSAAES.cxx
+++ b/common/rfb/SSecurityRSAAES.cxx
@@ -583,9 +583,10 @@ void SSecurityRSAAES::verifyUserPass()
#elif !defined(__APPLE__)
UnixPasswordValidator *valid = new UnixPasswordValidator();
#endif
- if (!valid->validate(sc, username, password)) {
+ std::string msg = "Authentication failed";
+ if (!valid->validate(sc, username, password, msg)) {
delete valid;
- throw auth_error("Authentication failed");
+ throw auth_error(msg);
}
delete valid;
#else
diff --git a/common/rfb/SSecurityTLS.cxx b/common/rfb/SSecurityTLS.cxx
index 2e173771..17497b8e 100644
--- a/common/rfb/SSecurityTLS.cxx
+++ b/common/rfb/SSecurityTLS.cxx
@@ -2,7 +2,7 @@
* Copyright (C) 2004 Red Hat Inc.
* Copyright (C) 2005 Martin Koegler
* Copyright (C) 2010 TigerVNC Team
- * Copyright (C) 2012-2021 Pierre Ossman for Cendio AB
+ * Copyright 2012-2025 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -37,8 +37,7 @@
#include <rfb/Exception.h>
#include <rdr/TLSException.h>
-#include <rdr/TLSInStream.h>
-#include <rdr/TLSOutStream.h>
+#include <rdr/TLSSocket.h>
#include <gnutls/x509.h>
@@ -72,7 +71,7 @@ static core::LogWriter vlog("TLS");
SSecurityTLS::SSecurityTLS(SConnection* sc_, bool _anon)
: SSecurity(sc_), session(nullptr), anon_cred(nullptr),
- cert_cred(nullptr), anon(_anon), tlsis(nullptr), tlsos(nullptr),
+ cert_cred(nullptr), anon(_anon), tlssock(nullptr),
rawis(nullptr), rawos(nullptr)
{
int ret;
@@ -88,32 +87,13 @@ SSecurityTLS::SSecurityTLS(SConnection* sc_, bool _anon)
void SSecurityTLS::shutdown()
{
- if (tlsos) {
- try {
- if (tlsos->hasBufferedData()) {
- tlsos->cork(false);
- tlsos->flush();
- if (tlsos->hasBufferedData())
- vlog.error("Failed to flush remaining socket data on close");
- }
- } catch (std::exception& e) {
- vlog.error("Failed to flush remaining socket data on close: %s", e.what());
- }
- }
-
- if (session) {
- int ret;
- // FIXME: We can't currently wait for the response, so we only send
- // our close and hope for the best
- ret = gnutls_bye(session, GNUTLS_SHUT_WR);
- if ((ret != GNUTLS_E_SUCCESS) && (ret != GNUTLS_E_INVALID_SESSION))
- vlog.error("TLS shutdown failed: %s", gnutls_strerror(ret));
- }
+ if (tlssock)
+ tlssock->shutdown();
#if defined (SSECURITYTLS__USE_DEPRECATED_DH)
if (dh_params) {
gnutls_dh_params_deinit(dh_params);
- dh_params = 0;
+ dh_params = nullptr;
}
#endif
@@ -133,13 +113,9 @@ void SSecurityTLS::shutdown()
rawos = nullptr;
}
- if (tlsis) {
- delete tlsis;
- tlsis = nullptr;
- }
- if (tlsos) {
- delete tlsos;
- tlsos = nullptr;
+ if (tlssock) {
+ delete tlssock;
+ tlssock = nullptr;
}
if (session) {
@@ -185,56 +161,46 @@ bool SSecurityTLS::processMsg()
os->writeU8(1);
os->flush();
- // Create these early as they set up the push/pull functions
- // for GnuTLS
- tlsis = new rdr::TLSInStream(is, session);
- tlsos = new rdr::TLSOutStream(os, session);
+ tlssock = new rdr::TLSSocket(is, os, session);
rawis = is;
rawos = os;
}
- err = gnutls_handshake(session);
- if (err != GNUTLS_E_SUCCESS) {
- if (!gnutls_error_is_fatal(err)) {
- vlog.debug("Deferring completion of TLS handshake: %s", gnutls_strerror(err));
+ try {
+ if (!tlssock->handshake())
return false;
- }
- vlog.error("TLS Handshake failed: %s", gnutls_strerror (err));
+ } catch (std::exception&) {
shutdown();
- throw rdr::tls_error("TLS Handshake failed", err);
+ throw;
}
vlog.debug("TLS handshake completed with %s",
gnutls_session_get_desc(session));
- sc->setStreams(tlsis, tlsos);
+ sc->setStreams(&tlssock->inStream(), &tlssock->outStream());
return true;
}
void SSecurityTLS::setParams()
{
- static const char kx_anon_priority[] = ":+ANON-ECDH:+ANON-DH";
+ static const char kx_anon_priority[] = "+ANON-ECDH:+ANON-DH";
int ret;
// Custom priority string specified?
if (strcmp(Security::GnuTLSPriority, "") != 0) {
- char *prio;
+ std::string prio;
const char *err;
- prio = new char[strlen(Security::GnuTLSPriority) +
- strlen(kx_anon_priority) + 1];
-
- strcpy(prio, Security::GnuTLSPriority);
- if (anon)
- strcat(prio, kx_anon_priority);
-
- ret = gnutls_priority_set_direct(session, prio, &err);
-
- delete [] prio;
+ prio = (const char*)Security::GnuTLSPriority;
+ if (anon) {
+ prio += ":";
+ prio += kx_anon_priority;
+ }
+ ret = gnutls_priority_set_direct(session, prio.c_str(), &err);
if (ret != GNUTLS_E_SUCCESS) {
if (ret == GNUTLS_E_INVALID_REQUEST)
vlog.error("GnuTLS priority syntax error at: %s", err);
@@ -244,30 +210,22 @@ void SSecurityTLS::setParams()
const char *err;
#if GNUTLS_VERSION_NUMBER >= 0x030603
- // gnutls_set_default_priority_appends() expects a normal priority string that
- // doesn't start with ":".
- ret = gnutls_set_default_priority_append(session, kx_anon_priority + 1, &err, 0);
+ ret = gnutls_set_default_priority_append(session, kx_anon_priority, &err, 0);
if (ret != GNUTLS_E_SUCCESS) {
if (ret == GNUTLS_E_INVALID_REQUEST)
vlog.error("GnuTLS priority syntax error at: %s", err);
throw rdr::tls_error("gnutls_set_default_priority_append()", ret);
}
#else
+ std::string prio;
+
// We don't know what the system default priority is, so we guess
// it's what upstream GnuTLS has
- static const char gnutls_default_priority[] = "NORMAL";
- char *prio;
-
- prio = new char[strlen(gnutls_default_priority) +
- strlen(kx_anon_priority) + 1];
-
- strcpy(prio, gnutls_default_priority);
- strcat(prio, kx_anon_priority);
-
- ret = gnutls_priority_set_direct(session, prio, &err);
-
- delete [] prio;
+ prio = "NORMAL";
+ prio += ":";
+ prio += kx_anon_priority;
+ ret = gnutls_priority_set_direct(session, prio.c_str(), &err);
if (ret != GNUTLS_E_SUCCESS) {
if (ret == GNUTLS_E_INVALID_REQUEST)
vlog.error("GnuTLS priority syntax error at: %s", err);
diff --git a/common/rfb/SSecurityTLS.h b/common/rfb/SSecurityTLS.h
index 7e80117c..61eec2a8 100644
--- a/common/rfb/SSecurityTLS.h
+++ b/common/rfb/SSecurityTLS.h
@@ -2,6 +2,7 @@
* Copyright (C) 2004 Red Hat Inc.
* Copyright (C) 2005 Martin Koegler
* Copyright (C) 2010 TigerVNC Team
+ * Copyright 2012-2025 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -41,8 +42,7 @@
namespace rdr {
class InStream;
class OutStream;
- class TLSInStream;
- class TLSOutStream;
+ class TLSSocket;
}
namespace rfb {
@@ -72,8 +72,7 @@ namespace rfb {
bool anon;
- rdr::TLSInStream* tlsis;
- rdr::TLSOutStream* tlsos;
+ rdr::TLSSocket* tlssock;
rdr::InStream* rawis;
rdr::OutStream* rawos;
diff --git a/common/rfb/ServerParams.cxx b/common/rfb/ServerParams.cxx
index e4691e19..4b8f6136 100644
--- a/common/rfb/ServerParams.cxx
+++ b/common/rfb/ServerParams.cxx
@@ -24,6 +24,7 @@
#include <stdexcept>
+#include <core/LogWriter.h>
#include <core/string.h>
#include <rfb/ledStates.h>
@@ -33,6 +34,8 @@
using namespace rfb;
+static core::LogWriter vlog("ServerParams");
+
ServerParams::ServerParams()
: majorVersion(0), minorVersion(0),
supportsQEMUKeyEvent(false),
@@ -67,8 +70,14 @@ void ServerParams::setDimensions(int width, int height)
void ServerParams::setDimensions(int width, int height, const ScreenSet& layout)
{
- if (!layout.validate(width, height))
+ if (!layout.validate(width, height)) {
+ char buffer[2048];
+ vlog.debug("Invalid screen layout for %dx%d:", width, height);
+ layout.print(buffer, sizeof(buffer));
+ vlog.debug("%s", buffer);
+
throw std::invalid_argument("Attempted to configure an invalid screen layout");
+ }
width_ = width;
height_ = height;
diff --git a/common/rfb/UnixPasswordValidator.cxx b/common/rfb/UnixPasswordValidator.cxx
index 36b8dd7a..8239463a 100644
--- a/common/rfb/UnixPasswordValidator.cxx
+++ b/common/rfb/UnixPasswordValidator.cxx
@@ -22,24 +22,119 @@
#include <config.h>
#endif
+#include <assert.h>
+#include <string.h>
+#include <security/pam_appl.h>
+
#include <core/Configuration.h>
+#include <core/LogWriter.h>
#include <rfb/UnixPasswordValidator.h>
-#include <rfb/pam.h>
using namespace rfb;
+static core::LogWriter vlog("UnixPasswordValidator");
+
static core::StringParameter pamService
("PAMService", "Service name for PAM password validation", "vnc");
core::AliasParameter pam_service("pam_service", "Alias for PAMService",
&pamService);
-int do_pam_auth(const char *service, const char *username,
- const char *password);
+std::string UnixPasswordValidator::displayName;
+
+typedef struct
+{
+ const char *username;
+ const char *password;
+ std::string &msg;
+} AuthData;
+
+#if defined(__sun)
+static int pam_callback(int count, struct pam_message **in,
+ struct pam_response **out, void *ptr)
+#else
+static int pam_callback(int count, const struct pam_message **in,
+ struct pam_response **out, void *ptr)
+#endif
+{
+ int i;
+ AuthData *auth = (AuthData *) ptr;
+ struct pam_response *resp =
+ (struct pam_response *) malloc (sizeof (struct pam_response) * count);
+
+ if (!resp && count)
+ return PAM_CONV_ERR;
+
+ for (i = 0; i < count; i++) {
+ resp[i].resp_retcode = PAM_SUCCESS;
+ switch (in[i]->msg_style) {
+ case PAM_TEXT_INFO:
+ vlog.info("%s info: %s", (const char *) pamService, in[i]->msg);
+ auth->msg = in[i]->msg;
+ resp[i].resp = nullptr;
+ break;
+ case PAM_ERROR_MSG:
+ vlog.error("%s error: %s", (const char *) pamService, in[i]->msg);
+ auth->msg = in[i]->msg;
+ resp[i].resp = nullptr;
+ break;
+ case PAM_PROMPT_ECHO_ON: /* Send Username */
+ resp[i].resp = strdup(auth->username);
+ break;
+ case PAM_PROMPT_ECHO_OFF: /* Send Password */
+ resp[i].resp = strdup(auth->password);
+ break;
+ default:
+ free(resp);
+ return PAM_CONV_ERR;
+ }
+ }
-bool UnixPasswordValidator::validateInternal(SConnection * /*sc*/,
+ *out = resp;
+ return PAM_SUCCESS;
+}
+
+bool UnixPasswordValidator::validateInternal(SConnection * /* sc */,
const char *username,
- const char *password)
+ const char *password,
+ std::string &msg)
{
- return do_pam_auth(pamService, username, password);
+ int ret;
+ AuthData auth = { username, password, msg };
+ struct pam_conv conv = {
+ pam_callback,
+ &auth
+ };
+ pam_handle_t *pamh = nullptr;
+ ret = pam_start(pamService, username, &conv, &pamh);
+ if (ret != PAM_SUCCESS) {
+ /* Can't call pam_strerror() here because the content of pamh undefined */
+ vlog.error("pam_start(%s) failed: %d", (const char *) pamService, ret);
+ return false;
+ }
+#ifdef PAM_XDISPLAY
+ /* At this point, displayName should never be empty */
+ assert(displayName.length() > 0);
+ /* Pass the display name to PAM modules but PAM_XDISPLAY may not be
+ * recognized by modules built with old versions of PAM */
+ ret = pam_set_item(pamh, PAM_XDISPLAY, displayName.c_str());
+ if (ret != PAM_SUCCESS && ret != PAM_BAD_ITEM) {
+ vlog.error("pam_set_item(PAM_XDISPLAY) failed: %d (%s)", ret, pam_strerror(pamh, ret));
+ goto error;
+ }
+#endif
+ ret = pam_authenticate(pamh, 0);
+ if (ret != PAM_SUCCESS) {
+ vlog.error("pam_authenticate() failed: %d (%s)", ret, pam_strerror(pamh, ret));
+ goto error;
+ }
+ ret = pam_acct_mgmt(pamh, 0);
+ if (ret != PAM_SUCCESS) {
+ vlog.error("pam_acct_mgmt() failed: %d (%s)", ret, pam_strerror(pamh, ret));
+ goto error;
+ }
+ return true;
+error:
+ pam_end(pamh, ret);
+ return false;
}
diff --git a/common/rfb/UnixPasswordValidator.h b/common/rfb/UnixPasswordValidator.h
index 4d623d6c..a2cc89c5 100644
--- a/common/rfb/UnixPasswordValidator.h
+++ b/common/rfb/UnixPasswordValidator.h
@@ -26,9 +26,19 @@
namespace rfb
{
class UnixPasswordValidator: public PasswordValidator {
+ public:
+ static void setDisplayName(const std::string& display) {
+ displayName = display;
+ }
+
protected:
- bool validateInternal(SConnection * sc, const char *username,
- const char *password) override;
+ bool validateInternal(SConnection *sc,
+ const char *username,
+ const char *password,
+ std::string &msg) override;
+
+ private:
+ static std::string displayName;
};
}
diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx
index 0b3537ad..2d77fae6 100644
--- a/common/rfb/VNCSConnectionST.cxx
+++ b/common/rfb/VNCSConnectionST.cxx
@@ -314,8 +314,6 @@ void VNCSConnectionST::requestClipboardOrClose()
{
try {
if (state() != RFBSTATE_NORMAL) return;
- if (!accessCheck(AccessCutText)) return;
- if (!rfb::Server::acceptCutText) return;
requestClipboard();
} catch(std::exception& e) {
close(e.what());
@@ -326,8 +324,6 @@ void VNCSConnectionST::announceClipboardOrClose(bool available)
{
try {
if (state() != RFBSTATE_NORMAL) return;
- if (!accessCheck(AccessCutText)) return;
- if (!rfb::Server::sendCutText) return;
announceClipboard(available);
} catch(std::exception& e) {
close(e.what());
@@ -338,8 +334,6 @@ void VNCSConnectionST::sendClipboardDataOrClose(const char* data)
{
try {
if (state() != RFBSTATE_NORMAL) return;
- if (!accessCheck(AccessCutText)) return;
- if (!rfb::Server::sendCutText) return;
sendClipboardData(data);
} catch(std::exception& e) {
close(e.what());
@@ -467,6 +461,7 @@ void VNCSConnectionST::setPixelFormat(const PixelFormat& pf)
pf.print(buffer, 256);
vlog.info("Client pixel format %s", buffer);
setCursor();
+ encodeManager.forceRefresh(server->getPixelBuffer()->getRect());
}
void VNCSConnectionST::pointerEvent(const core::Point& pos,
@@ -476,7 +471,6 @@ void VNCSConnectionST::pointerEvent(const core::Point& pos,
idleTimer.start(core::secsToMillis(rfb::Server::idleTimeout));
pointerEventTime = time(nullptr);
if (!accessCheck(AccessPtrEvents)) return;
- if (!rfb::Server::acceptPointerEvents) return;
pointerEventPos = pos;
server->pointerEvent(this, pointerEventPos, buttonMask);
}
@@ -509,6 +503,8 @@ void VNCSConnectionST::keyEvent(uint32_t keysym, uint32_t keycode, bool down) {
if (rfb::Server::idleTimeout)
idleTimer.start(core::secsToMillis(rfb::Server::idleTimeout));
if (!accessCheck(AccessKeyEvents)) return;
+ // FIXME: This check isn't strictly needed, but we get a lot of
+ // confusing debug logging without it
if (!rfb::Server::acceptKeyEvents) return;
if (down)
@@ -662,8 +658,7 @@ void VNCSConnectionST::setDesktopSize(int fb_width, int fb_height,
layout.print(buffer, sizeof(buffer));
vlog.debug("%s", buffer);
- if (!accessCheck(AccessSetDesktopSize) ||
- !rfb::Server::acceptSetDesktopSize) {
+ if (!accessCheck(AccessSetDesktopSize)) {
vlog.debug("Rejecting unauthorized framebuffer resize request");
result = resultProhibited;
} else {
@@ -744,21 +739,16 @@ void VNCSConnectionST::enableContinuousUpdates(bool enable,
void VNCSConnectionST::handleClipboardRequest()
{
- if (!accessCheck(AccessCutText)) return;
server->handleClipboardRequest(this);
}
void VNCSConnectionST::handleClipboardAnnounce(bool available)
{
- if (!accessCheck(AccessCutText)) return;
- if (!rfb::Server::acceptCutText) return;
server->handleClipboardAnnounce(this, available);
}
void VNCSConnectionST::handleClipboardData(const char* data)
{
- if (!accessCheck(AccessCutText)) return;
- if (!rfb::Server::acceptCutText) return;
server->handleClipboardData(this, data);
}
diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx
index cce04164..77d652b9 100644
--- a/common/rfb/VNCServerST.cxx
+++ b/common/rfb/VNCServerST.cxx
@@ -364,6 +364,9 @@ void VNCServerST::setScreenLayout(const ScreenSet& layout)
void VNCServerST::requestClipboard()
{
+ if (!rfb::Server::acceptCutText)
+ return;
+
if (clipboardClient == nullptr) {
slog.debug("Got request for client clipboard but no client currently owns the clipboard");
return;
@@ -378,6 +381,9 @@ void VNCServerST::announceClipboard(bool available)
clipboardRequestors.clear();
+ if (!rfb::Server::sendCutText)
+ return;
+
for (ci = clients.begin(); ci != clients.end(); ++ci)
(*ci)->announceClipboardOrClose(available);
}
@@ -386,6 +392,9 @@ void VNCServerST::sendClipboardData(const char* data)
{
std::list<VNCSConnectionST*>::iterator ci;
+ if (!rfb::Server::sendCutText)
+ return;
+
if (strchr(data, '\r') != nullptr)
throw std::invalid_argument("Invalid carriage return in clipboard data");
@@ -478,6 +487,9 @@ void VNCServerST::setLEDState(unsigned int state)
void VNCServerST::keyEvent(uint32_t keysym, uint32_t keycode, bool down)
{
+ if (!rfb::Server::acceptKeyEvents)
+ return;
+
if (rfb::Server::maxIdleTime)
idleTimer.start(core::secsToMillis(rfb::Server::maxIdleTime));
@@ -500,6 +512,10 @@ void VNCServerST::pointerEvent(VNCSConnectionST* client,
uint16_t buttonMask)
{
time_t now = time(nullptr);
+
+ if (!rfb::Server::acceptPointerEvents)
+ return;
+
if (rfb::Server::maxIdleTime)
idleTimer.start(core::secsToMillis(rfb::Server::maxIdleTime));
@@ -529,9 +545,11 @@ void VNCServerST::handleClipboardRequest(VNCSConnectionST* client)
void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client,
bool available)
{
- if (available)
+ if (available) {
+ if (!rfb::Server::acceptCutText)
+ return;
clipboardClient = client;
- else {
+ } else {
if (client != clipboardClient)
return;
clipboardClient = nullptr;
@@ -542,6 +560,8 @@ void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client,
void VNCServerST::handleClipboardData(VNCSConnectionST* client,
const char* data)
{
+ if (!rfb::Server::acceptCutText)
+ return;
if (client != clipboardClient) {
slog.debug("Ignoring unexpected clipboard data");
return;
@@ -556,6 +576,11 @@ unsigned int VNCServerST::setDesktopSize(VNCSConnectionST* requester,
unsigned int result;
std::list<VNCSConnectionST*>::iterator ci;
+ if (!rfb::Server::acceptSetDesktopSize) {
+ slog.debug("Rejecting unauthorized framebuffer resize request");
+ return resultProhibited;
+ }
+
// We can't handle a framebuffer larger than this, so don't let a
// client set one (see PixelBuffer.cxx)
if ((fb_width > 16384) || (fb_height > 16384)) {
diff --git a/common/rfb/WinPasswdValidator.cxx b/common/rfb/WinPasswdValidator.cxx
index 84832e81..a6281950 100644
--- a/common/rfb/WinPasswdValidator.cxx
+++ b/common/rfb/WinPasswdValidator.cxx
@@ -30,7 +30,8 @@ using namespace rfb;
// This method will only work for Windows NT, 2000, and XP (and possibly Vista)
bool WinPasswdValidator::validateInternal(rfb::SConnection* /*sc*/,
const char* username,
- const char* password)
+ const char* password,
+ std::string & /* msg */)
{
HANDLE handle;
diff --git a/common/rfb/WinPasswdValidator.h b/common/rfb/WinPasswdValidator.h
index 340a6234..993cafea 100644
--- a/common/rfb/WinPasswdValidator.h
+++ b/common/rfb/WinPasswdValidator.h
@@ -21,6 +21,7 @@
#ifndef __RFB_WINPASSWDVALIDATOR_H__
#define __RFB_WINPASSWDVALIDATOR_H__
+#include <string>
#include <rfb/SSecurityPlain.h>
namespace rfb
@@ -30,7 +31,10 @@ namespace rfb
WinPasswdValidator() {};
virtual ~WinPasswdValidator() {};
protected:
- bool validateInternal(SConnection *sc, const char* username, const char* password) override;
+ bool validateInternal(SConnection *sc,
+ const char *username,
+ const char *password,
+ std::string &msg) override;
};
}
diff --git a/common/rfb/pam.c b/common/rfb/pam.c
deleted file mode 100644
index f9e5ce74..00000000
--- a/common/rfb/pam.c
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2006 Martin Koegler
- * Copyright (C) 2010 TigerVNC Team
- *
- * 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.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <security/pam_appl.h>
-
-#include <rfb/pam.h>
-
-typedef struct
-{
- const char *username;
- const char *password;
-} AuthData;
-
-#if defined(__sun)
-static int pam_callback(int count, struct pam_message **in,
- struct pam_response **out, void *ptr)
-#else
-static int pam_callback(int count, const struct pam_message **in,
- struct pam_response **out, void *ptr)
-#endif
-{
- int i;
- AuthData *auth = (AuthData *) ptr;
- struct pam_response *resp =
- (struct pam_response *) malloc (sizeof (struct pam_response) * count);
-
- if (!resp && count)
- return PAM_CONV_ERR;
-
- for (i = 0; i < count; i++) {
- resp[i].resp_retcode = PAM_SUCCESS;
- switch (in[i]->msg_style) {
- case PAM_TEXT_INFO:
- case PAM_ERROR_MSG:
- resp[i].resp = 0;
- break;
- case PAM_PROMPT_ECHO_ON: /* Send Username */
- resp[i].resp = strdup(auth->username);
- break;
- case PAM_PROMPT_ECHO_OFF: /* Send Password */
- resp[i].resp = strdup(auth->password);
- break;
- default:
- free(resp);
- return PAM_CONV_ERR;
- }
- }
-
- *out = resp;
- return PAM_SUCCESS;
-}
-
-
-int do_pam_auth(const char *service, const char *username, const char *password)
-{
- int ret;
- AuthData auth = { username, password };
- struct pam_conv conv = {
- pam_callback,
- &auth
- };
- pam_handle_t *h = 0;
- ret = pam_start(service, username, &conv, &h);
- if (ret == PAM_SUCCESS)
- ret = pam_authenticate(h, 0);
- if (ret == PAM_SUCCESS)
- ret = pam_acct_mgmt(h, 0);
- pam_end(h, ret);
-
- return ret == PAM_SUCCESS ? 1 : 0;
-}
-
diff --git a/common/rfb/pam.h b/common/rfb/pam.h
deleted file mode 100644
index d378d19c..00000000
--- a/common/rfb/pam.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2006 Martin Koegler
- * Copyright (C) 2010 TigerVNC Team
- *
- * 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.
- */
-
-#ifndef __RFB_PAM_H__
-#define __RFB_PAM_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-int do_pam_auth(const char *service, const char *username, const char *password);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif