diff options
| author | 2021-11-10 00:54:35 -0500 | |
|---|---|---|
| committer | 2021-11-10 00:54:35 -0500 | |
| commit | 58fb7a0cee13d84170aac52f3f89d91888e1afe3 (patch) | |
| tree | 1d6312ba15f9ece5a8031e5280dfb8b38be8dfa3 /src/lib/net | |
| parent | 28db84b46139c9bb2bbcac8c6cc56e71d1e35629 (diff) | |
| parent | beb08eb751fa8e1f72042f263316ab5e5ddb596d (diff) | |
Update upstream source from tag 'upstream/2.4.0+dfsg'
Update to upstream version '2.4.0+dfsg'
with Debian dir 4b6668cc08c7b0e56b1e1f7b4e89ecdb316182a0
Diffstat (limited to 'src/lib/net')
31 files changed, 1042 insertions, 381 deletions
diff --git a/src/lib/net/CMakeLists.txt b/src/lib/net/CMakeLists.txt index 5439450..8c12ed9 100644 --- a/src/lib/net/CMakeLists.txt +++ b/src/lib/net/CMakeLists.txt @@ -1,11 +1,11 @@ # barrier -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton -# +# # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # found in the file LICENSE that should have accompanied this file. -# +# # This package 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 diff --git a/src/lib/net/ConnectionSecurityLevel.h b/src/lib/net/ConnectionSecurityLevel.h new file mode 100644 index 0000000..913c2fd --- /dev/null +++ b/src/lib/net/ConnectionSecurityLevel.h @@ -0,0 +1,27 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package 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 program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef BARRIER_LIB_NET_CONNECTION_SECURITY_LEVEL_H +#define BARRIER_LIB_NET_CONNECTION_SECURITY_LEVEL_H + +enum class ConnectionSecurityLevel { + PLAINTEXT, + ENCRYPTED, + ENCRYPTED_AUTHENTICATED +}; + +#endif // BARRIER_LIB_NET_CONNECTION_SECURITY_LEVEL_H diff --git a/src/lib/net/FingerprintData.cpp b/src/lib/net/FingerprintData.cpp new file mode 100644 index 0000000..460b39b --- /dev/null +++ b/src/lib/net/FingerprintData.cpp @@ -0,0 +1,52 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package 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 program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "base/String.h" +#include "FingerprintDatabase.h" +#include "io/filesystem.h" +#include <algorithm> +#include <fstream> + +namespace barrier { + +bool FingerprintData::operator==(const FingerprintData& other) const +{ + return algorithm == other.algorithm && data == other.data; +} + +const char* fingerprint_type_to_string(FingerprintType type) +{ + switch (type) { + case FingerprintType::INVALID: return "invalid"; + case FingerprintType::SHA1: return "sha1"; + case FingerprintType::SHA256: return "sha256"; + } + return "invalid"; +} + +FingerprintType fingerprint_type_from_string(const std::string& type) +{ + if (type == "sha1") { + return FingerprintType::SHA1; + } + if (type == "sha256") { + return FingerprintType::SHA256; + } + return FingerprintType::INVALID; +} + +} // namespace barrier diff --git a/src/lib/net/FingerprintData.h b/src/lib/net/FingerprintData.h new file mode 100644 index 0000000..938a695 --- /dev/null +++ b/src/lib/net/FingerprintData.h @@ -0,0 +1,46 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package 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 program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef BARRIER_LIB_NET_FINGERPRINT_DATA_H +#define BARRIER_LIB_NET_FINGERPRINT_DATA_H + +#include <string> +#include <vector> + +namespace barrier { + +enum FingerprintType { + INVALID, + SHA1, // deprecated + SHA256, +}; + +struct FingerprintData { + std::string algorithm; + std::vector<std::uint8_t> data; + + bool valid() const { return !algorithm.empty(); } + + bool operator==(const FingerprintData& other) const; +}; + +const char* fingerprint_type_to_string(FingerprintType type); +FingerprintType fingerprint_type_from_string(const std::string& type); + +} // namespace barrier + +#endif // BARRIER_LIB_NET_FINGERPRINT_TYPE_H diff --git a/src/lib/net/FingerprintDatabase.cpp b/src/lib/net/FingerprintDatabase.cpp new file mode 100644 index 0000000..def0de9 --- /dev/null +++ b/src/lib/net/FingerprintDatabase.cpp @@ -0,0 +1,135 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package 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 program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "base/String.h" +#include "FingerprintDatabase.h" +#include "io/filesystem.h" +#include <algorithm> +#include <fstream> + +namespace barrier { + +void FingerprintDatabase::read(const fs::path& path) +{ + std::ifstream file; + open_utf8_path(file, path, std::ios_base::in); + read_stream(file); +} + +void FingerprintDatabase::write(const fs::path& path) +{ + std::ofstream file; + open_utf8_path(file, path, std::ios_base::out); + write_stream(file); +} + +void FingerprintDatabase::read_stream(std::istream& stream) +{ + if (!stream.good()) { + return; + } + + std::string line; + while (std::getline(stream, line)) { + if (line.empty()) { + continue; + } + + auto fingerprint = parse_db_line(line); + if (!fingerprint.valid()) { + continue; + } + + fingerprints_.push_back(fingerprint); + } +} + +void FingerprintDatabase::write_stream(std::ostream& stream) +{ + if (!stream.good()) { + return; + } + + for (const auto& fingerprint : fingerprints_) { + stream << to_db_line(fingerprint) << "\n"; + } +} + +void FingerprintDatabase::clear() +{ + fingerprints_.clear(); +} + +void FingerprintDatabase::add_trusted(const FingerprintData& fingerprint) +{ + if (is_trusted(fingerprint)) { + return; + } + fingerprints_.push_back(fingerprint); +} + +bool FingerprintDatabase::is_trusted(const FingerprintData& fingerprint) +{ + auto found_it = std::find(fingerprints_.begin(), fingerprints_.end(), fingerprint); + return found_it != fingerprints_.end(); +} + +FingerprintData FingerprintDatabase::parse_db_line(const std::string& line) +{ + FingerprintData result; + + // legacy v1 certificate handling + if (std::count(line.begin(), line.end(), ':') == 19 && line.size() == 40 + 19) { + auto data = string::from_hex(line); + if (data.empty()) { + return result; + } + result.algorithm = fingerprint_type_to_string(FingerprintType::SHA1); + result.data = data; + return result; + } + + auto version_end_pos = line.find(':'); + if (version_end_pos == std::string::npos) { + return result; + } + if (line.substr(0, version_end_pos) != "v2") { + return result; + } + auto algo_start_pos = version_end_pos + 1; + auto algo_end_pos = line.find(':', algo_start_pos); + if (algo_end_pos == std::string::npos) { + return result; + } + auto algorithm = line.substr(algo_start_pos, algo_end_pos - algo_start_pos); + auto data = string::from_hex(line.substr(algo_end_pos + 1)); + + if (data.empty()) { + return result; + } + + result.algorithm = algorithm; + result.data = data; + return result; +} + +std::string FingerprintDatabase::to_db_line(const FingerprintData& fingerprint) +{ + return "v2:" + fingerprint.algorithm + ":" + string::to_hex(fingerprint.data, 2); +} + +} // namespace barrier diff --git a/src/lib/net/FingerprintDatabase.h b/src/lib/net/FingerprintDatabase.h new file mode 100644 index 0000000..4927265 --- /dev/null +++ b/src/lib/net/FingerprintDatabase.h @@ -0,0 +1,53 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package 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 program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef BARRIER_LIB_NET_FINGERPRINT_DATABASE_H +#define BARRIER_LIB_NET_FINGERPRINT_DATABASE_H + +#include "FingerprintData.h" +#include "io/filesystem.h" +#include <iosfwd> +#include <string> +#include <vector> + +namespace barrier { + +class FingerprintDatabase { +public: + void read(const fs::path& path); + void write(const fs::path& path); + + void read_stream(std::istream& stream); + void write_stream(std::ostream& stream); + + void clear(); + void add_trusted(const FingerprintData& fingerprint); + bool is_trusted(const FingerprintData& fingerprint); + + const std::vector<FingerprintData>& fingerprints() const { return fingerprints_; } + + static FingerprintData parse_db_line(const std::string& line); + static std::string to_db_line(const FingerprintData& fingerprint); + +private: + + std::vector<FingerprintData> fingerprints_; +}; + +} // namespace barrier + +#endif // BARRIER_LIB_NET_FINGERPRINT_DATABASE_H diff --git a/src/lib/net/IDataSocket.cpp b/src/lib/net/IDataSocket.cpp index cc679c3..30e00f4 100644 --- a/src/lib/net/IDataSocket.cpp +++ b/src/lib/net/IDataSocket.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 diff --git a/src/lib/net/IDataSocket.h b/src/lib/net/IDataSocket.h index c77a99c..0fc41bb 100644 --- a/src/lib/net/IDataSocket.h +++ b/src/lib/net/IDataSocket.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 diff --git a/src/lib/net/IListenSocket.h b/src/lib/net/IListenSocket.h index 73dcc6e..5452a37 100644 --- a/src/lib/net/IListenSocket.h +++ b/src/lib/net/IListenSocket.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 @@ -41,7 +41,7 @@ public: */ virtual IDataSocket* accept() = 0; - + //@} // ISocket overrides diff --git a/src/lib/net/ISocket.h b/src/lib/net/ISocket.h index 0e9688b..f41fd55 100644 --- a/src/lib/net/ISocket.h +++ b/src/lib/net/ISocket.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 diff --git a/src/lib/net/ISocketFactory.h b/src/lib/net/ISocketFactory.h index e440953..edfc8c9 100644 --- a/src/lib/net/ISocketFactory.h +++ b/src/lib/net/ISocketFactory.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 @@ -20,6 +20,7 @@ #include "common/IInterface.h" #include "arch/IArchNetwork.h" +#include "net/ConnectionSecurityLevel.h" class IDataSocket; class IListenSocket; @@ -35,14 +36,12 @@ public: //@{ //! Create data socket - virtual IDataSocket* create( - IArchNetwork::EAddressFamily family, - bool secure) const = 0; + virtual IDataSocket* create(IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) const = 0; //! Create listen socket - virtual IListenSocket* createListen( - IArchNetwork::EAddressFamily family, - bool secure) const = 0; + virtual IListenSocket* createListen(IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) const = 0; //@} }; diff --git a/src/lib/net/ISocketMultiplexerJob.h b/src/lib/net/ISocketMultiplexerJob.h index c27fce2..2dab87e 100644 --- a/src/lib/net/ISocketMultiplexerJob.h +++ b/src/lib/net/ISocketMultiplexerJob.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 diff --git a/src/lib/net/NetworkAddress.cpp b/src/lib/net/NetworkAddress.cpp index c8ea9c6..3526fc0 100644 --- a/src/lib/net/NetworkAddress.cpp +++ b/src/lib/net/NetworkAddress.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 diff --git a/src/lib/net/NetworkAddress.h b/src/lib/net/NetworkAddress.h index 87dc1e4..3a006af 100644 --- a/src/lib/net/NetworkAddress.h +++ b/src/lib/net/NetworkAddress.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 @@ -43,7 +43,7 @@ public: If \c hostname can be parsed as a numerical address then that's how it's used, otherwise it's used as a host name. If \c hostname ends in ":[0-9]+" then that suffix is extracted and used as the port, - overridding the port parameter. The resulting port must be a valid + overriding the port parameter. The resulting port must be a valid port number (zero is not a valid port number) otherwise \c XSocketAddress is thrown with an error of \c XSocketAddress::kBadPort. The hostname is not resolved by the c'tor; use \c resolve to do that. diff --git a/src/lib/net/SecureListenSocket.cpp b/src/lib/net/SecureListenSocket.cpp index a137f39..11efc5c 100644 --- a/src/lib/net/SecureListenSocket.cpp +++ b/src/lib/net/SecureListenSocket.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2015-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 @@ -20,23 +20,16 @@ #include "SecureSocket.h" #include "net/NetworkAddress.h" #include "net/SocketMultiplexer.h" -#include "net/TSocketMultiplexerMethodJob.h" +#include "arch/Arch.h" #include "arch/XArch.h" #include "common/DataDirectories.h" #include "base/String.h" -static const char s_certificateDir[] = { "SSL" }; -static const char s_certificateFilename[] = { "Barrier.pem" }; - -// -// SecureListenSocket -// - -SecureListenSocket::SecureListenSocket( - IEventQueue* events, - SocketMultiplexer* socketMultiplexer, - IArchNetwork::EAddressFamily family) : - TCPListenSocket(events, socketMultiplexer, family) +SecureListenSocket::SecureListenSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, + IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) : + TCPListenSocket(events, socketMultiplexer, family), + security_level_{security_level} { } @@ -45,22 +38,15 @@ SecureListenSocket::accept() { SecureSocket* socket = NULL; try { - socket = new SecureSocket( - m_events, - m_socketMultiplexer, - ARCH->acceptSocket(m_socket, NULL)); + socket = new SecureSocket(m_events, m_socketMultiplexer, + ARCH->acceptSocket(m_socket, NULL), security_level_); socket->initSsl(true); if (socket != NULL) { setListeningJob(); } - std::string certificateFilename = barrier::string::sprintf("%s/%s/%s", - DataDirectories::profile().c_str(), - s_certificateDir, - s_certificateFilename); - - bool loaded = socket->loadCertificates(certificateFilename); + bool loaded = socket->load_certificates(barrier::DataDirectories::ssl_certificate_path()); if (!loaded) { delete socket; return NULL; diff --git a/src/lib/net/SecureListenSocket.h b/src/lib/net/SecureListenSocket.h index d0c6e23..a0e792a 100644 --- a/src/lib/net/SecureListenSocket.h +++ b/src/lib/net/SecureListenSocket.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2015-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 @@ -19,18 +19,21 @@ #include "net/TCPListenSocket.h" #include "common/stdset.h" +#include "ConnectionSecurityLevel.h" class IEventQueue; class SocketMultiplexer; class IDataSocket; -class SecureListenSocket : public TCPListenSocket{ +class SecureListenSocket : public TCPListenSocket { public: - SecureListenSocket(IEventQueue* events, - SocketMultiplexer* socketMultiplexer, - IArchNetwork::EAddressFamily family); + SecureListenSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, + IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level); // IListenSocket overrides virtual IDataSocket* accept(); +private: + ConnectionSecurityLevel security_level_; }; diff --git a/src/lib/net/SecureSocket.cpp b/src/lib/net/SecureSocket.cpp index 855e16b..85e1d38 100644 --- a/src/lib/net/SecureSocket.cpp +++ b/src/lib/net/SecureSocket.cpp @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2015-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 @@ -16,6 +16,7 @@ */ #include "SecureSocket.h" +#include "SecureUtils.h" #include "net/TSocketMultiplexerMethodJob.h" #include "base/TMethodEventJob.h" @@ -25,6 +26,8 @@ #include "base/Log.h" #include "base/String.h" #include "common/DataDirectories.h" +#include "io/filesystem.h" +#include "net/FingerprintDatabase.h" #include <openssl/ssl.h> #include <openssl/err.h> @@ -40,41 +43,36 @@ #define MAX_ERROR_SIZE 65535 +static const std::size_t MAX_INPUT_BUFFER_SIZE = 1024 * 1024; static const float s_retryDelay = 0.01f; enum { kMsgSize = 128 }; -static const char kFingerprintDirName[] = "SSL/Fingerprints"; -//static const char kFingerprintLocalFilename[] = "Local.txt"; -static const char kFingerprintTrustedServersFilename[] = "TrustedServers.txt"; -//static const char kFingerprintTrustedClientsFilename[] = "TrustedClients.txt"; - struct Ssl { SSL_CTX* m_context; SSL* m_ssl; }; -SecureSocket::SecureSocket( - IEventQueue* events, - SocketMultiplexer* socketMultiplexer, - IArchNetwork::EAddressFamily family) : +SecureSocket::SecureSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, + IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) : TCPSocket(events, socketMultiplexer, family), m_ssl(nullptr), m_secureReady(false), - m_fatal(false) + m_fatal(false), + security_level_{security_level} { } -SecureSocket::SecureSocket( - IEventQueue* events, - SocketMultiplexer* socketMultiplexer, - ArchSocket socket) : +SecureSocket::SecureSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, + ArchSocket socket, ConnectionSecurityLevel security_level) : TCPSocket(events, socketMultiplexer, socket), m_ssl(nullptr), m_secureReady(false), - m_fatal(false) + m_fatal(false), + security_level_{security_level} { } @@ -103,6 +101,8 @@ SecureSocket::close() void SecureSocket::freeSSLResources() { + std::lock_guard<std::mutex> ssl_lock{ssl_mutex_}; + if (m_ssl->m_ssl != NULL) { SSL_shutdown(m_ssl->m_ssl); SSL_free(m_ssl->m_ssl); @@ -133,30 +133,30 @@ std::unique_ptr<ISocketMultiplexerJob> SecureSocket::newJob() if (m_connected && !m_secureReady) { return {}; } - + return TCPSocket::newJob(); } void SecureSocket::secureConnect() { - setJob(std::make_unique<TSocketMultiplexerMethodJob<SecureSocket>>( - this, &SecureSocket::serviceConnect, - getSocket(), isReadable(), isWritable())); + setJob(std::make_unique<TSocketMultiplexerMethodJob>([this](auto j, auto r, auto w, auto e) + { return serviceConnect(j, r, w, e); }, + getSocket(), isReadable(), isWritable())); } void SecureSocket::secureAccept() { - setJob(std::make_unique<TSocketMultiplexerMethodJob<SecureSocket>>( - this, &SecureSocket::serviceAccept, - getSocket(), isReadable(), isWritable())); + setJob(std::make_unique<TSocketMultiplexerMethodJob>([this](auto j, auto r, auto w, auto e) + { return serviceAccept(j, r, w, e); }, + getSocket(), isReadable(), isWritable())); } TCPSocket::EJobResult SecureSocket::doRead() { - static UInt8 buffer[4096]; + UInt8 buffer[4096]; memset(buffer, 0, sizeof(buffer)); int bytesRead = 0; int status = 0; @@ -173,20 +173,24 @@ SecureSocket::doRead() else { return kRetry; } - + if (bytesRead > 0) { bool wasEmpty = (m_inputBuffer.getSize() == 0); - + // slurp up as much as possible do { m_inputBuffer.write(buffer, bytesRead); - + + if (m_inputBuffer.getSize() > MAX_INPUT_BUFFER_SIZE) { + break; + } + status = secureRead(buffer, sizeof(buffer), bytesRead); if (status < 0) { return kBreak; } } while (bytesRead > 0 || status > 0); - + // send input ready if input buffer was empty if (wasEmpty) { sendEvent(m_events->forIStream().inputReady()); @@ -204,18 +208,13 @@ SecureSocket::doRead() m_readable = false; return kNew; } - + return kRetry; } TCPSocket::EJobResult SecureSocket::doWrite() { - static bool s_retry = false; - static int s_retrySize = 0; - static std::unique_ptr<char[]> s_staticBuffer; - static std::size_t s_staticBufferSize = 0; - // write data int bufferSize = 0; int bytesWrote = 0; @@ -224,34 +223,34 @@ SecureSocket::doWrite() if (!isSecureReady()) return kRetry; - if (s_retry) { - bufferSize = s_retrySize; + if (do_write_retry_) { + bufferSize = do_write_retry_size_; } else { bufferSize = m_outputBuffer.getSize(); - if (bufferSize > s_staticBufferSize) { - s_staticBuffer.reset(new char[bufferSize]); - s_staticBufferSize = bufferSize; + if (bufferSize > do_write_retry_buffer_size_) { + do_write_retry_buffer_.reset(new char[bufferSize]); + do_write_retry_buffer_size_ = bufferSize; } if (bufferSize > 0) { - memcpy(s_staticBuffer.get(), m_outputBuffer.peek(bufferSize), bufferSize); + std::memcpy(do_write_retry_buffer_.get(), m_outputBuffer.peek(bufferSize), bufferSize); } } - + if (bufferSize == 0) { return kRetry; } - status = secureWrite(s_staticBuffer.get(), bufferSize, bytesWrote); + status = secureWrite(do_write_retry_buffer_.get(), bufferSize, bytesWrote); if (status > 0) { - s_retry = false; + do_write_retry_ = false; } else if (status < 0) { return kBreak; } else if (status == 0) { - s_retry = true; - s_retrySize = bufferSize; + do_write_retry_ = true; + do_write_retry_size_ = bufferSize; return kNew; } - + if (bytesWrote > 0) { discardWrittenData(bytesWrote); return kNew; @@ -263,16 +262,16 @@ SecureSocket::doWrite() int SecureSocket::secureRead(void* buffer, int size, int& read) { + std::lock_guard<std::mutex> ssl_lock{ssl_mutex_}; + if (m_ssl->m_ssl != NULL) { LOG((CLOG_DEBUG2 "reading secure socket")); read = SSL_read(m_ssl->m_ssl, buffer, size); - - static int retry; // Check result will cleanup the connection in the case of a fatal - checkResult(read, retry); - - if (retry) { + checkResult(read, secure_read_retry_); + + if (secure_read_retry_) { return 0; } @@ -289,17 +288,17 @@ SecureSocket::secureRead(void* buffer, int size, int& read) int SecureSocket::secureWrite(const void* buffer, int size, int& wrote) { + std::lock_guard<std::mutex> ssl_lock{ssl_mutex_}; + if (m_ssl->m_ssl != NULL) { LOG((CLOG_DEBUG2 "writing secure socket:%p", this)); wrote = SSL_write(m_ssl->m_ssl, buffer, size); - - static int retry; // Check result will cleanup the connection in the case of a fatal - checkResult(wrote, retry); + checkResult(wrote, secure_write_retry_); - if (retry) { + if (secure_write_retry_) { return 0; } @@ -322,6 +321,8 @@ SecureSocket::isSecureReady() void SecureSocket::initSsl(bool server) { + std::lock_guard<std::mutex> ssl_lock{ssl_mutex_}; + m_ssl = new Ssl(); m_ssl->m_context = NULL; m_ssl->m_ssl = NULL; @@ -329,54 +330,57 @@ SecureSocket::initSsl(bool server) initContext(server); } -bool SecureSocket::loadCertificates(std::string& filename) +bool SecureSocket::load_certificates(const barrier::fs::path& path) { - if (filename.empty()) { + std::lock_guard<std::mutex> ssl_lock{ssl_mutex_}; + + if (path.empty()) { showError("ssl certificate is not specified"); return false; } else { - std::ifstream file(filename.c_str()); - bool exist = file.good(); - file.close(); - - if (!exist) { - std::string errorMsg("ssl certificate doesn't exist: "); - errorMsg.append(filename); - showError(errorMsg.c_str()); + if (!barrier::fs::is_regular_file(path)) { + showError("ssl certificate doesn't exist: " + path.u8string()); return false; } } int r = 0; - r = SSL_CTX_use_certificate_file(m_ssl->m_context, filename.c_str(), SSL_FILETYPE_PEM); + r = SSL_CTX_use_certificate_file(m_ssl->m_context, path.u8string().c_str(), SSL_FILETYPE_PEM); if (r <= 0) { - showError("could not use ssl certificate"); + showError("could not use ssl certificate: " + path.u8string()); return false; } - r = SSL_CTX_use_PrivateKey_file(m_ssl->m_context, filename.c_str(), SSL_FILETYPE_PEM); + r = SSL_CTX_use_PrivateKey_file(m_ssl->m_context, path.u8string().c_str(), SSL_FILETYPE_PEM); if (r <= 0) { - showError("could not use ssl private key"); + showError("could not use ssl private key: " + path.u8string()); return false; } r = SSL_CTX_check_private_key(m_ssl->m_context); if (!r) { - showError("could not verify ssl private key"); + showError("could not verify ssl private key: " + path.u8string()); return false; } return true; } +static int cert_verify_ignore_callback(X509_STORE_CTX*, void*) +{ + return 1; +} + void SecureSocket::initContext(bool server) { + // ssl_mutex_ is assumed to be acquired + SSL_library_init(); const SSL_METHOD* method; - + // load & register all cryptos, etc. OpenSSL_add_all_algorithms(); @@ -394,7 +398,7 @@ SecureSocket::initContext(bool server) else { method = SSLv23_client_method(); } - + // create new context from method SSL_METHOD* m = const_cast<SSL_METHOD*>(method); m_ssl->m_context = SSL_CTX_new(m); @@ -403,13 +407,23 @@ SecureSocket::initContext(bool server) SSL_CTX_set_options(m_ssl->m_context, SSL_OP_NO_SSLv3); if (m_ssl->m_context == NULL) { - showError(); + showError(""); + } + + if (security_level_ == ConnectionSecurityLevel::ENCRYPTED_AUTHENTICATED) { + // We want to ask for peer certificate, but not verify it. If we don't ask for peer + // certificate, e.g. client won't send it. + SSL_CTX_set_verify(m_ssl->m_context, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + nullptr); + SSL_CTX_set_cert_verify_callback(m_ssl->m_context, cert_verify_ignore_callback, nullptr); } } void SecureSocket::createSSL() { + // ssl_mutex_ is assumed to be acquired + // I assume just one instance is needed // get new SSL state with context if (m_ssl->m_ssl == NULL) { @@ -421,17 +435,17 @@ SecureSocket::createSSL() int SecureSocket::secureAccept(int socket) { + std::lock_guard<std::mutex> ssl_lock{ssl_mutex_}; + createSSL(); // set connection socket to SSL state SSL_set_fd(m_ssl->m_ssl, socket); - + LOG((CLOG_DEBUG2 "accepting secure socket")); int r = SSL_accept(m_ssl->m_ssl); - - static int retry; - checkResult(r, retry); + checkResult(r, secure_accept_retry_); if (isFatal()) { // tell user and sleep so the socket isn't hammered. @@ -439,12 +453,30 @@ SecureSocket::secureAccept(int socket) LOG((CLOG_INFO "client connection may not be secure")); m_secureReady = false; ARCH->sleep(1); - retry = 0; + secure_accept_retry_ = 0; return -1; // Failed, error out } // If not fatal and no retry, state is good - if (retry == 0) { + if (secure_accept_retry_ == 0) { + if (security_level_ == ConnectionSecurityLevel::ENCRYPTED_AUTHENTICATED) { + if (verify_cert_fingerprint( + barrier::DataDirectories::trusted_clients_ssl_fingerprints_path())) { + LOG((CLOG_INFO "accepted secure socket")); + if (!ensure_peer_certificate()) { + secure_accept_retry_ = 0; + disconnect(); + return -1;// Cert fail, error + } + } + else { + LOG((CLOG_ERR "failed to verify server certificate fingerprint")); + secure_accept_retry_ = 0; + disconnect(); + return -1; // Fingerprint failed, error + } + } + m_secureReady = true; LOG((CLOG_INFO "accepted secure socket")); if (CLOG->getFilter() >= kDEBUG1) { @@ -455,7 +487,7 @@ SecureSocket::secureAccept(int socket) } // If not fatal and retry is set, not ready, and return retry - if (retry > 0) { + if (secure_accept_retry_ > 0) { LOG((CLOG_DEBUG2 "retry accepting secure socket")); m_secureReady = false; ARCH->sleep(s_retryDelay); @@ -470,38 +502,45 @@ SecureSocket::secureAccept(int socket) int SecureSocket::secureConnect(int socket) { + // note that load_certificates acquires ssl_mutex_ + if (!load_certificates(barrier::DataDirectories::ssl_certificate_path())) { + LOG((CLOG_ERR "could not load client certificates")); + // FIXME: this is fatal error, but we current don't disconnect because whole logic in this + // function needs to be cleaned up + } + + std::lock_guard<std::mutex> ssl_lock{ssl_mutex_}; + createSSL(); // attach the socket descriptor SSL_set_fd(m_ssl->m_ssl, socket); - + LOG((CLOG_DEBUG2 "connecting secure socket")); int r = SSL_connect(m_ssl->m_ssl); - - static int retry; - checkResult(r, retry); + checkResult(r, secure_connect_retry_); if (isFatal()) { LOG((CLOG_ERR "failed to connect secure socket")); - retry = 0; + secure_connect_retry_ = 0; return -1; } // If we should retry, not ready and return 0 - if (retry > 0) { + if (secure_connect_retry_ > 0) { LOG((CLOG_DEBUG2 "retry connect secure socket")); m_secureReady = false; ARCH->sleep(s_retryDelay); return 0; } - retry = 0; + secure_connect_retry_ = 0; // No error, set ready, process and return ok m_secureReady = true; - if (verifyCertFingerprint()) { + if (verify_cert_fingerprint(barrier::DataDirectories::trusted_servers_ssl_fingerprints_path())) { LOG((CLOG_INFO "connected to secure socket")); - if (!showCertificate()) { + if (!ensure_peer_certificate()) { disconnect(); return -1;// Cert fail, error } @@ -520,21 +559,22 @@ SecureSocket::secureConnect(int socket) } bool -SecureSocket::showCertificate() +SecureSocket::ensure_peer_certificate() { + // ssl_mutex_ is assumed to be acquired X509* cert; char* line; - + // get the server's certificate cert = SSL_get_peer_certificate(m_ssl->m_ssl); if (cert != NULL) { line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); - LOG((CLOG_INFO "server ssl certificate info: %s", line)); + LOG((CLOG_INFO "peer ssl certificate info: %s", line)); OPENSSL_free(line); X509_free(cert); } else { - showError("server has no ssl certificate"); + showError("peer has no ssl certificate"); return false; } @@ -544,6 +584,8 @@ SecureSocket::showCertificate() void SecureSocket::checkResult(int status, int& retry) { + // ssl_mutex_ is assumed to be acquired + // ssl errors are a little quirky. the "want" errors are normal and // should result in a retry. @@ -568,7 +610,7 @@ SecureSocket::checkResult(int status, int& retry) case SSL_ERROR_WANT_WRITE: // Need to make sure the socket is known to be writable so the impending - // select action actually triggers on a write. This isn't necessary for + // select action actually triggers on a write. This isn't necessary for // m_readable because the socket logic is always readable m_writable = true; retry++; @@ -618,16 +660,15 @@ SecureSocket::checkResult(int status, int& retry) if (isFatal()) { retry = 0; - showError(); + showError(""); disconnect(); } } -void -SecureSocket::showError(const char* reason) +void SecureSocket::showError(const std::string& reason) { - if (reason != NULL) { - LOG((CLOG_ERR "%s", reason)); + if (!reason.empty()) { + LOG((CLOG_ERR "%s", reason.c_str())); } std::string error = getError(); @@ -658,83 +699,49 @@ SecureSocket::disconnect() sendEvent(getEvents()->forIStream().inputShutdown()); } -void SecureSocket::formatFingerprint(std::string& fingerprint, bool hex, bool separator) +bool SecureSocket::verify_cert_fingerprint(const barrier::fs::path& fingerprint_db_path) { - if (hex) { - // to hexidecimal - barrier::string::toHex(fingerprint, 2); - } - - // all uppercase - barrier::string::uppercase(fingerprint); + // ssl_mutex_ is assumed to be acquired - if (separator) { - // add colon to separate each 2 charactors - size_t separators = fingerprint.size() / 2; - for (size_t i = 1; i < separators; i++) { - fingerprint.insert(i * 3 - 1, ":"); - } - } -} - -bool -SecureSocket::verifyCertFingerprint() -{ // calculate received certificate fingerprint - X509 *cert = cert = SSL_get_peer_certificate(m_ssl->m_ssl); - EVP_MD* tempDigest; - unsigned char tempFingerprint[EVP_MAX_MD_SIZE]; - unsigned int tempFingerprintLen; - tempDigest = (EVP_MD*)EVP_sha1(); - int digestResult = X509_digest(cert, tempDigest, tempFingerprint, &tempFingerprintLen); - - if (digestResult <= 0) { - LOG((CLOG_ERR "failed to calculate fingerprint, digest result: %d", digestResult)); + barrier::FingerprintData fingerprint_sha1, fingerprint_sha256; + try { + auto* cert = SSL_get_peer_certificate(m_ssl->m_ssl); + fingerprint_sha1 = barrier::get_ssl_cert_fingerprint(cert, + barrier::FingerprintType::SHA1); + fingerprint_sha256 = barrier::get_ssl_cert_fingerprint(cert, + barrier::FingerprintType::SHA256); + } catch (const std::exception& e) { + LOG((CLOG_ERR "%s", e.what())); return false; } - // format fingerprint into hexdecimal format with colon separator - std::string fingerprint(reinterpret_cast<char*>(tempFingerprint), tempFingerprintLen); - formatFingerprint(fingerprint); - LOG((CLOG_NOTE "server fingerprint: %s", fingerprint.c_str())); - - std::string trustedServersFilename; - trustedServersFilename = barrier::string::sprintf( - "%s/%s/%s", - DataDirectories::profile().c_str(), - kFingerprintDirName, - kFingerprintTrustedServersFilename); + // note: the GUI parses the following two lines of logs, don't change unnecessarily + LOG((CLOG_NOTE "peer fingerprint (SHA1): %s (SHA256): %s", + barrier::format_ssl_fingerprint(fingerprint_sha1.data).c_str(), + barrier::format_ssl_fingerprint(fingerprint_sha256.data).c_str())); // Provide debug hint as to what file is being used to verify fingerprint trust - LOG((CLOG_NOTE "trustedServersFilename: %s", trustedServersFilename.c_str() )); + LOG((CLOG_NOTE "fingerprint_db_path: %s", fingerprint_db_path.u8string().c_str())); - // check if this fingerprint exist - std::string fileLine; - std::ifstream file; - file.open(trustedServersFilename.c_str()); + barrier::FingerprintDatabase db; + db.read(fingerprint_db_path); - if (!file.is_open()) { - LOG((CLOG_NOTE "Unable to open trustedServersFile: %s", trustedServersFilename.c_str() )); + if (!db.fingerprints().empty()) { + LOG((CLOG_NOTE "Read %d fingerprints from: %s", db.fingerprints().size(), + fingerprint_db_path.u8string().c_str())); } else { - LOG((CLOG_NOTE "Opened trustedServersFilename: %s", trustedServersFilename.c_str() )); + LOG((CLOG_NOTE "Could not read fingerprints from: %s", + fingerprint_db_path.u8string().c_str())); } - bool isValid = false; - while (!file.eof() && file.is_open()) { - getline(file,fileLine); - if (!fileLine.empty()) { - if (fileLine.compare(fingerprint) == 0) { - LOG((CLOG_NOTE "Fingerprint matches trusted fingerprint")); - isValid = true; - break; - } else { - LOG((CLOG_NOTE "Fingerprint does not match trusted fingerprint")); - } - } + if (db.is_trusted(fingerprint_sha256)) { + LOG((CLOG_NOTE "Fingerprint matches trusted fingerprint")); + return true; + } else { + LOG((CLOG_NOTE "Fingerprint does not match trusted fingerprint")); + return false; } - - file.close(); - return isValid; } MultiplexerJobStatus SecureSocket::serviceConnect(ISocketMultiplexerJob* job, @@ -765,9 +772,9 @@ MultiplexerJobStatus SecureSocket::serviceConnect(ISocketMultiplexerJob* job, // Retry case return { true, - std::make_unique<TSocketMultiplexerMethodJob<SecureSocket>>( - this, &SecureSocket::serviceConnect, - getSocket(), isReadable(), isWritable()) + std::make_unique<TSocketMultiplexerMethodJob>([this](auto j, auto r, auto w, auto e) + { return serviceConnect(j, r, w, e); }, + getSocket(), isReadable(), isWritable()) }; } @@ -795,9 +802,12 @@ MultiplexerJobStatus SecureSocket::serviceAccept(ISocketMultiplexerJob* job, } // Retry case - return {true, std::make_unique<TSocketMultiplexerMethodJob<SecureSocket>>( - this, &SecureSocket::serviceAccept, - getSocket(), isReadable(), isWritable())}; + return { + true, + std::make_unique<TSocketMultiplexerMethodJob>([this](auto j, auto r, auto w, auto e) + { return serviceAccept(j, r, w, e); }, + getSocket(), isReadable(), isWritable()) + }; } void @@ -822,6 +832,8 @@ showCipherStackDesc(STACK_OF(SSL_CIPHER) * stack) { void SecureSocket::showSecureCipherInfo() { + // ssl_mutex_ is assumed to be acquired + STACK_OF(SSL_CIPHER) * sStack = SSL_get_ciphers(m_ssl->m_ssl); if (sStack == NULL) { @@ -832,8 +844,8 @@ SecureSocket::showSecureCipherInfo() showCipherStackDesc(sStack); } -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) - // m_ssl->m_ssl->session->ciphers is not forward compatable, +#if OPENSSL_VERSION_NUMBER < 0x10100000L + // m_ssl->m_ssl->session->ciphers is not forward compatible, // In future release of OpenSSL, it's not visible, STACK_OF(SSL_CIPHER) * cStack = m_ssl->m_ssl->session->ciphers; #else @@ -864,6 +876,8 @@ SecureSocket::showSecureLibInfo() void SecureSocket::showSecureConnectInfo() { + // ssl_mutex_ is assumed to be acquired + const SSL_CIPHER* cipher = SSL_get_current_cipher(m_ssl->m_ssl); if (cipher != NULL) { diff --git a/src/lib/net/SecureSocket.h b/src/lib/net/SecureSocket.h index c602e2d..be7dc0d 100644 --- a/src/lib/net/SecureSocket.h +++ b/src/lib/net/SecureSocket.h @@ -1,11 +1,11 @@ /* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2015-2016 Symless Ltd. - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 @@ -17,8 +17,11 @@ #pragma once +#include "ConnectionSecurityLevel.h" #include "net/TCPSocket.h" #include "net/XSocket.h" +#include "io/filesystem.h" +#include <mutex> class IEventQueue; class SocketMultiplexer; @@ -32,10 +35,10 @@ A secure socket using SSL. */ class SecureSocket : public TCPSocket { public: - SecureSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, IArchNetwork::EAddressFamily family); - SecureSocket(IEventQueue* events, - SocketMultiplexer* socketMultiplexer, - ArchSocket socket); + SecureSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, + IArchNetwork::EAddressFamily family, ConnectionSecurityLevel security_level); + SecureSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, + ArchSocket socket, ConnectionSecurityLevel security_level); ~SecureSocket(); // ISocket overrides @@ -43,7 +46,7 @@ public: // IDataSocket overrides virtual void connect(const NetworkAddress&) override; - + std::unique_ptr<ISocketMultiplexerJob> newJob() override; bool isFatal() const override { return m_fatal; } void isFatal(bool b) { m_fatal = b; } @@ -55,35 +58,56 @@ public: EJobResult doRead() override; EJobResult doWrite() override; void initSsl(bool server); - bool loadCertificates(std::string& CertFile); + bool load_certificates(const barrier::fs::path& path); private: // SSL - void initContext(bool server); - void createSSL(); + void initContext(bool server); // may only be called with ssl_mutex_ acquired + void createSSL(); // may only be called with ssl_mutex_ acquired. int secureAccept(int s); int secureConnect(int s); - bool showCertificate(); - void checkResult(int n, int& retry); - void showError(const char* reason = NULL); + bool ensure_peer_certificate(); // may only be called with ssl_mutex_ acquired + + void checkResult(int n, int& retry); // may only be called with m_ssl_mutex_ acquired. + + void showError(const std::string& reason); std::string getError(); void disconnect(); - void formatFingerprint(std::string& fingerprint, bool hex = true, bool separator = true); - bool verifyCertFingerprint(); + + // may only be called with ssl_mutex_ acquired + bool verify_cert_fingerprint(const barrier::fs::path& fingerprint_db_path); MultiplexerJobStatus serviceConnect(ISocketMultiplexerJob*, bool, bool, bool); MultiplexerJobStatus serviceAccept(ISocketMultiplexerJob*, bool, bool, bool); - void showSecureConnectInfo(); - void showSecureLibInfo(); - void showSecureCipherInfo(); - + void showSecureConnectInfo(); // may only be called with ssl_mutex_ acquired + void showSecureLibInfo(); + void showSecureCipherInfo(); // may only be called with ssl_mutex_ acquired + void handleTCPConnected(const Event& event, void*); void freeSSLResources(); private: + // all accesses to m_ssl must be protected by this mutex. The only function that is called + // from outside SocketMultiplexer thread is close(), so we mostly care about things accessed + // by it. + std::mutex ssl_mutex_; + Ssl* m_ssl; bool m_secureReady; bool m_fatal; + ConnectionSecurityLevel security_level_ = ConnectionSecurityLevel::ENCRYPTED; + + int secure_accept_retry_ = 0; // used only in secureAccept() + int secure_connect_retry_ = 0; // used only in secureConnect() + int secure_read_retry_ = 0; // used only in secureRead() + int secure_write_retry_ = 0; // used only in secureWrite() + + // The following are used only from doWrite() + // FIXME: using std::vector would simplify logic significantly. + bool do_write_retry_ = false; + int do_write_retry_size_ = 0; + std::unique_ptr<char[]> do_write_retry_buffer_; + std::size_t do_write_retry_buffer_size_ = 0; }; diff --git a/src/lib/net/SecureUtils.cpp b/src/lib/net/SecureUtils.cpp new file mode 100644 index 0000000..c581dd4 --- /dev/null +++ b/src/lib/net/SecureUtils.cpp @@ -0,0 +1,312 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package 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 program. If not, see <http://www.gnu.org/licenses/>. + + ----------------------------------------------------------------------- + create_fingerprint_randomart() has been taken from the OpenSSH project. + Copyright information follows. + + Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. + Copyright (c) 2008 Alexander von Gernler. All rights reserved. + Copyright (c) 2010,2011 Damien Miller. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "SecureUtils.h" +#include "base/String.h" +#include "base/finally.h" +#include "io/filesystem.h" + +#include <openssl/evp.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/pem.h> +#include <algorithm> +#include <cstdio> +#include <cstring> +#include <stdexcept> + +#if SYSAPI_WIN32 +// Windows builds require a shim that makes it possible to link to different +// versions of the Win32 C runtime. See OpenSSL FAQ. +#include <openssl/applink.c> +#endif + +namespace barrier { + +namespace { + +const EVP_MD* get_digest_for_type(FingerprintType type) +{ + switch (type) { + case FingerprintType::SHA1: return EVP_sha1(); + case FingerprintType::SHA256: return EVP_sha256(); + } + throw std::runtime_error("Unknown fingerprint type " + std::to_string(static_cast<int>(type))); +} + +} // namespace + +std::string format_ssl_fingerprint(const std::vector<uint8_t>& fingerprint, bool separator) +{ + std::string result = barrier::string::to_hex(fingerprint, 2); + + // all uppercase + barrier::string::uppercase(result); + + if (separator) { + // add colon to separate each 2 characters + size_t separators = result.size() / 2; + for (size_t i = 1; i < separators; i++) { + result.insert(i * 3 - 1, ":"); + } + } + return result; +} + +std::string format_ssl_fingerprint_columns(const std::vector<uint8_t>& fingerprint) +{ + auto max_columns = 8; + + std::string hex = barrier::string::to_hex(fingerprint, 2); + barrier::string::uppercase(hex); + if (hex.empty() || hex.size() % 2 != 0) { + return hex; + } + + std::string separated; + for (std::size_t i = 0; i < hex.size(); i += max_columns * 2) { + for (std::size_t j = i; j < i + 16 && j < hex.size() - 1; j += 2) { + separated.push_back(hex[j]); + separated.push_back(hex[j + 1]); + separated.push_back(':'); + } + separated.push_back('\n'); + } + separated.pop_back(); // we don't need last newline character + return separated; +} + +FingerprintData get_ssl_cert_fingerprint(X509* cert, FingerprintType type) +{ + if (!cert) { + throw std::runtime_error("certificate is null"); + } + + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int digest_length = 0; + int result = X509_digest(cert, get_digest_for_type(type), digest, &digest_length); + + if (result <= 0) { + throw std::runtime_error("failed to calculate fingerprint, digest result: " + + std::to_string(result)); + } + + std::vector<std::uint8_t> digest_vec; + digest_vec.assign(reinterpret_cast<std::uint8_t*>(digest), + reinterpret_cast<std::uint8_t*>(digest) + digest_length); + return {fingerprint_type_to_string(type), digest_vec}; +} + +FingerprintData get_pem_file_cert_fingerprint(const std::string& path, FingerprintType type) +{ + auto fp = fopen_utf8_path(path, "r"); + if (!fp) { + throw std::runtime_error("Could not open certificate path"); + } + auto file_close = finally([fp]() { std::fclose(fp); }); + + X509* cert = PEM_read_X509(fp, nullptr, nullptr, nullptr); + if (!cert) { + throw std::runtime_error("Certificate could not be parsed"); + } + auto cert_free = finally([cert]() { X509_free(cert); }); + + return get_ssl_cert_fingerprint(cert, type); +} + +void generate_pem_self_signed_cert(const std::string& path) +{ + auto expiration_days = 365; + + auto* private_key = EVP_PKEY_new(); + if (!private_key) { + throw std::runtime_error("Could not allocate private key for certificate"); + } + auto private_key_free = finally([private_key](){ EVP_PKEY_free(private_key); }); + + auto* rsa = RSA_generate_key(2048, RSA_F4, nullptr, nullptr); + if (!rsa) { + throw std::runtime_error("Failed to generate RSA key"); + } + EVP_PKEY_assign_RSA(private_key, rsa); + + auto* cert = X509_new(); + if (!cert) { + throw std::runtime_error("Could not allocate certificate"); + } + auto cert_free = finally([cert]() { X509_free(cert); }); + + ASN1_INTEGER_set(X509_get_serialNumber(cert), 1); + X509_gmtime_adj(X509_get_notBefore(cert), 0); + X509_gmtime_adj(X509_get_notAfter(cert), expiration_days * 24 * 3600); + X509_set_pubkey(cert, private_key); + + auto* name = X509_get_subject_name(cert); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, + reinterpret_cast<const unsigned char *>("Barrier"), -1, -1, 0); + X509_set_issuer_name(cert, name); + + X509_sign(cert, private_key, EVP_sha256()); + + auto fp = fopen_utf8_path(path.c_str(), "r"); + if (!fp) { + throw std::runtime_error("Could not open certificate output path"); + } + auto file_close = finally([fp]() { std::fclose(fp); }); + + PEM_write_PrivateKey(fp, private_key, nullptr, nullptr, 0, nullptr, nullptr); + PEM_write_X509(fp, cert); +} + +/* + Draw an ASCII-Art representing the fingerprint so human brain can + profit from its built-in pattern recognition ability. + This technique is called "random art" and can be found in some + scientific publications like this original paper: + + "Hash Visualization: a New Technique to improve Real-World Security", + Perrig A. and Song D., 1999, International Workshop on Cryptographic + Techniques and E-Commerce (CrypTEC '99) + sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf + + The subject came up in a talk by Dan Kaminsky, too. + + If you see the picture is different, the key is different. + If the picture looks the same, you still know nothing. + + The algorithm used here is a worm crawling over a discrete plane, + leaving a trace (augmenting the field) everywhere it goes. + Movement is taken from dgst_raw 2bit-wise. Bumping into walls + makes the respective movement vector be ignored for this turn. + Graphs are not unambiguous, because circles in graphs can be +walked in either direction. + */ + +/* + Field sizes for the random art. Have to be odd, so the starting point + can be in the exact middle of the picture, and FLDBASE should be >=8 . + Else pictures would be too dense, and drawing the frame would + fail, too, because the key type would not fit in anymore. +*/ +#define FLDBASE 8 +#define FLDSIZE_Y (FLDBASE + 1) +#define FLDSIZE_X (FLDBASE * 2 + 1) + +std::string create_fingerprint_randomart(const std::vector<std::uint8_t>& dgst_raw) +{ + /* + * Chars to be used after each other every time the worm + * intersects with itself. Matter of taste. + */ + const char* augmentation_string = " .o+=*BOX@%&#/^SE"; + char *p; + std::uint8_t field[FLDSIZE_X][FLDSIZE_Y]; + std::size_t i; + std::uint32_t b; + int x, y; + std::size_t len = strlen(augmentation_string) - 1; + + std::vector<char> retval; + retval.reserve((FLDSIZE_X + 3) * (FLDSIZE_Y + 2)); + + auto add_char = [&retval](char ch) { retval.push_back(ch); }; + + /* initialize field */ + std::memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char)); + x = FLDSIZE_X / 2; + y = FLDSIZE_Y / 2; + + /* process raw key */ + for (i = 0; i < dgst_raw.size(); i++) { + /* each byte conveys four 2-bit move commands */ + int input = dgst_raw[i]; + for (b = 0; b < 4; b++) { + /* evaluate 2 bit, rest is shifted later */ + x += (input & 0x1) ? 1 : -1; + y += (input & 0x2) ? 1 : -1; + + /* assure we are still in bounds */ + x = std::max(x, 0); + y = std::max(y, 0); + x = std::min(x, FLDSIZE_X - 1); + y = std::min(y, FLDSIZE_Y - 1); + + /* augment the field */ + if (field[x][y] < len - 2) + field[x][y]++; + input = input >> 2; + } + } + + /* mark starting point and end point*/ + field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1; + field[x][y] = len; + + /* output upper border */ + add_char('+'); + for (i = 0; i < FLDSIZE_X; i++) + add_char('-'); + add_char('+'); + add_char('\n'); + + /* output content */ + for (y = 0; y < FLDSIZE_Y; y++) { + add_char('|'); + for (x = 0; x < FLDSIZE_X; x++) + add_char(augmentation_string[std::min<int>(field[x][y], len)]); + add_char('|'); + add_char('\n'); + } + + /* output lower border */ + add_char('+'); + for (i = 0; i < FLDSIZE_X; i++) + add_char('-'); + add_char('+'); + + return std::string{retval.data(), retval.size()}; +} + +} // namespace barrier diff --git a/src/lib/net/SecureUtils.h b/src/lib/net/SecureUtils.h new file mode 100644 index 0000000..c4d51f3 --- /dev/null +++ b/src/lib/net/SecureUtils.h @@ -0,0 +1,43 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package 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 program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef BARRIER_LIB_NET_SECUREUTILS_H +#define BARRIER_LIB_NET_SECUREUTILS_H + +#include "FingerprintData.h" +#include <openssl/ossl_typ.h> +#include <cstdint> +#include <string> +#include <vector> + +namespace barrier { + +std::string format_ssl_fingerprint(const std::vector<std::uint8_t>& fingerprint, + bool separator = true); +std::string format_ssl_fingerprint_columns(const std::vector<uint8_t>& fingerprint); + +FingerprintData get_ssl_cert_fingerprint(X509* cert, FingerprintType type); + +FingerprintData get_pem_file_cert_fingerprint(const std::string& path, FingerprintType type); + +void generate_pem_self_signed_cert(const std::string& path); + +std::string create_fingerprint_randomart(const std::vector<std::uint8_t>& dgst_raw); + +} // namespace barrier + +#endif // BARRIER_LIB_NET_SECUREUTILS_H diff --git a/src/lib/net/SocketMultiplexer.cpp b/src/lib/net/SocketMultiplexer.cpp index cdb9039..5a2328e 100644 --- a/src/lib/net/SocketMultiplexer.cpp +++ b/src/lib/net/SocketMultiplexer.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 @@ -26,7 +26,6 @@ #include "arch/Arch.h" #include "arch/XArch.h" #include "base/Log.h" -#include "base/TMethodJob.h" #include "common/stdvector.h" // @@ -58,8 +57,7 @@ SocketMultiplexer::SocketMultiplexer() : m_jobListLockLocker(NULL) { // start thread - m_thread = new Thread(new TMethodJob<SocketMultiplexer>( - this, &SocketMultiplexer::serviceThread)); + m_thread = new Thread([this](){ service_thread(); }); } SocketMultiplexer::~SocketMultiplexer() @@ -95,7 +93,7 @@ void SocketMultiplexer::addSocket(ISocket* socket, std::unique_ptr<ISocketMultip if (i == m_socketJobMap.end()) { // we *must* put the job at the end so the order of jobs in // the list continue to match the order of jobs in pfds in - // serviceThread(). + // service_thread(). JobCursor j = m_socketJobs.insert(m_socketJobs.end(), std::move(job)); m_update = true; m_socketJobMap.insert(std::make_pair(socket, j)); @@ -125,7 +123,7 @@ SocketMultiplexer::removeSocket(ISocket* socket) // remove job. rather than removing it from the map we put NULL // in the list instead so the order of jobs in the list continues - // to match the order of jobs in pfds in serviceThread(). + // to match the order of jobs in pfds in service_thread(). SocketJobMap::iterator i = m_socketJobMap.find(socket); if (i != m_socketJobMap.end()) { if (*(i->second)) { @@ -138,8 +136,7 @@ SocketMultiplexer::removeSocket(ISocket* socket) unlockJobList(); } -void -SocketMultiplexer::serviceThread(void*) +void SocketMultiplexer::service_thread() { std::vector<IArchNetwork::PollEntry> pfds; IArchNetwork::PollEntry pfd; @@ -179,7 +176,7 @@ SocketMultiplexer::serviceThread(void*) pfd.m_events |= IArchNetwork::kPOLLOUT; } pfds.push_back(pfd); - } + } jobCursor = nextCursor(cursor); } deleteCursor(cursor); diff --git a/src/lib/net/SocketMultiplexer.h b/src/lib/net/SocketMultiplexer.h index 9891558..b13c725 100644 --- a/src/lib/net/SocketMultiplexer.h +++ b/src/lib/net/SocketMultiplexer.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 @@ -67,7 +67,7 @@ private: // and m_update while m_pollable and m_polling are true. all other // threads must only modify these when m_pollable and m_polling are // false. only the service thread sets m_polling. - void serviceThread(void*); + void service_thread(); // create, iterate, and destroy a cursor. a cursor is used to // safely iterate through the job list while other threads modify diff --git a/src/lib/net/TCPListenSocket.cpp b/src/lib/net/TCPListenSocket.cpp index 26f03b0..2c305fc 100644 --- a/src/lib/net/TCPListenSocket.cpp +++ b/src/lib/net/TCPListenSocket.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 @@ -70,8 +70,10 @@ TCPListenSocket::bind(const NetworkAddress& addr) ARCH->bindSocket(m_socket, addr.getAddress()); ARCH->listenOnSocket(m_socket); - auto new_job = std::make_unique<TSocketMultiplexerMethodJob<TCPListenSocket>>( - this, &TCPListenSocket::serviceListening, m_socket, true, false); + auto new_job = std::make_unique<TSocketMultiplexerMethodJob>( + [this](auto j, auto r, auto w, auto e) + { return serviceListening(j, r, w, e); }, + m_socket, true, false); m_socketMultiplexer->addSocket(this, std::move(new_job)); } @@ -136,8 +138,10 @@ TCPListenSocket::accept() void TCPListenSocket::setListeningJob() { - auto new_job = std::make_unique<TSocketMultiplexerMethodJob<TCPListenSocket>>( - this, &TCPListenSocket::serviceListening, m_socket, true, false); + auto new_job = std::make_unique<TSocketMultiplexerMethodJob>( + [this](auto j, auto r, auto w, auto e) + { return serviceListening(j, r, w, e); }, + m_socket, true, false); m_socketMultiplexer->addSocket(this, std::move(new_job)); } diff --git a/src/lib/net/TCPListenSocket.h b/src/lib/net/TCPListenSocket.h index f3ababc..109c1c3 100644 --- a/src/lib/net/TCPListenSocket.h +++ b/src/lib/net/TCPListenSocket.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 diff --git a/src/lib/net/TCPSocket.cpp b/src/lib/net/TCPSocket.cpp index 09a8f17..fa7edcc 100644 --- a/src/lib/net/TCPSocket.cpp +++ b/src/lib/net/TCPSocket.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 @@ -33,9 +33,7 @@ #include <cstdlib> #include <memory> -// -// TCPSocket -// +static const std::size_t MAX_INPUT_BUFFER_SIZE = 1024 * 1024; TCPSocket::TCPSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, IArchNetwork::EAddressFamily family) : IDataSocket(events), @@ -335,19 +333,23 @@ TCPSocket::doRead() UInt8 buffer[4096]; memset(buffer, 0, sizeof(buffer)); size_t bytesRead = 0; - + bytesRead = ARCH->readSocket(m_socket, buffer, sizeof(buffer)); - + if (bytesRead > 0) { bool wasEmpty = (m_inputBuffer.getSize() == 0); - + // slurp up as much as possible do { m_inputBuffer.write(buffer, (UInt32)bytesRead); + if (m_inputBuffer.getSize() > MAX_INPUT_BUFFER_SIZE) { + break; + } + bytesRead = ARCH->readSocket(m_socket, buffer, sizeof(buffer)); } while (bytesRead > 0); - + // send input ready if input buffer was empty if (wasEmpty) { sendEvent(m_events->forIStream().inputReady()); @@ -365,7 +367,7 @@ TCPSocket::doRead() m_readable = false; return kNew; } - + return kRetry; } @@ -384,7 +386,7 @@ TCPSocket::doWrite() discardWrittenData(bytesWrote); return kNew; } - + return kRetry; } @@ -424,18 +426,20 @@ std::unique_ptr<ISocketMultiplexerJob> TCPSocket::newJob() if (!(m_readable || m_writable)) { return {}; } - return std::make_unique<TSocketMultiplexerMethodJob<TCPSocket>>( - this, &TCPSocket::serviceConnecting, - m_socket, m_readable, m_writable); + return std::make_unique<TSocketMultiplexerMethodJob>( + [this](auto j, auto r, auto w, auto e) + { return serviceConnecting(j, r, w, e); }, + m_socket, m_readable, m_writable); } else { - if (!(m_readable || (m_writable && (m_outputBuffer.getSize() > 0)))) { + auto writable = m_writable && (m_outputBuffer.getSize() > 0); + if (!(m_readable || writable)) { return {}; } - return std::make_unique<TSocketMultiplexerMethodJob<TCPSocket>>( - this, &TCPSocket::serviceConnected, - m_socket, m_readable, - m_writable && (m_outputBuffer.getSize() > 0)); + return std::make_unique<TSocketMultiplexerMethodJob>( + [this](auto j, auto r, auto w, auto e) + { return serviceConnected(j, r, w, e); }, + m_socket, m_readable, writable); } } diff --git a/src/lib/net/TCPSocket.h b/src/lib/net/TCPSocket.h index 0b98888..45aed07 100644 --- a/src/lib/net/TCPSocket.h +++ b/src/lib/net/TCPSocket.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 @@ -59,7 +59,7 @@ public: // IDataSocket overrides virtual void connect(const NetworkAddress&); - + virtual std::unique_ptr<ISocketMultiplexerJob> newJob(); protected: @@ -68,7 +68,7 @@ protected: kRetry, //!< Retry the same job kNew //!< Require a new job }; - + ArchSocket getSocket() { return m_socket; } IEventQueue* getEvents() { return m_events; } virtual EJobResult doRead(); @@ -105,7 +105,7 @@ protected: IEventQueue* m_events; StreamBuffer m_inputBuffer; StreamBuffer m_outputBuffer; - + private: Mutex m_mutex; ArchSocket m_socket; diff --git a/src/lib/net/TCPSocketFactory.cpp b/src/lib/net/TCPSocketFactory.cpp index 6ff4ef8..30e930e 100644 --- a/src/lib/net/TCPSocketFactory.cpp +++ b/src/lib/net/TCPSocketFactory.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 @@ -40,11 +40,12 @@ TCPSocketFactory::~TCPSocketFactory() // do nothing } -IDataSocket* -TCPSocketFactory::create(IArchNetwork::EAddressFamily family, bool secure) const +IDataSocket* TCPSocketFactory::create(IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) const { - if (secure) { - SecureSocket* secureSocket = new SecureSocket(m_events, m_socketMultiplexer, family); + if (security_level != ConnectionSecurityLevel::PLAINTEXT) { + SecureSocket* secureSocket = new SecureSocket(m_events, m_socketMultiplexer, family, + security_level); secureSocket->initSsl (false); return secureSocket; } @@ -53,12 +54,12 @@ TCPSocketFactory::create(IArchNetwork::EAddressFamily family, bool secure) const } } -IListenSocket* -TCPSocketFactory::createListen(IArchNetwork::EAddressFamily family, bool secure) const +IListenSocket* TCPSocketFactory::createListen(IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) const { IListenSocket* socket = NULL; - if (secure) { - socket = new SecureListenSocket(m_events, m_socketMultiplexer, family); + if (security_level != ConnectionSecurityLevel::PLAINTEXT) { + socket = new SecureListenSocket(m_events, m_socketMultiplexer, family, security_level); } else { socket = new TCPListenSocket(m_events, m_socketMultiplexer, family); diff --git a/src/lib/net/TCPSocketFactory.h b/src/lib/net/TCPSocketFactory.h index 0195ec4..ac21cab 100644 --- a/src/lib/net/TCPSocketFactory.h +++ b/src/lib/net/TCPSocketFactory.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 @@ -31,12 +31,11 @@ public: virtual ~TCPSocketFactory(); // ISocketFactory overrides - virtual IDataSocket* create( - IArchNetwork::EAddressFamily family, - bool secure) const; - virtual IListenSocket* createListen( - IArchNetwork::EAddressFamily family, - bool secure) const; + virtual IDataSocket* create(IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) const; + + virtual IListenSocket* createListen(IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) const; private: IEventQueue* m_events; diff --git a/src/lib/net/TSocketMultiplexerMethodJob.h b/src/lib/net/TSocketMultiplexerMethodJob.h index 9e74cdd..4b571ab 100644 --- a/src/lib/net/TSocketMultiplexerMethodJob.h +++ b/src/lib/net/TSocketMultiplexerMethodJob.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 @@ -25,81 +25,43 @@ /*! A socket multiplexer job class that invokes a member function. */ -template <class T> class TSocketMultiplexerMethodJob : public ISocketMultiplexerJob { public: - using Method = MultiplexerJobStatus (T::*)(ISocketMultiplexerJob*, bool, bool, bool); + using RunFunction = std::function<MultiplexerJobStatus(ISocketMultiplexerJob*, bool, bool, bool)>; //! run() invokes \c object->method(arg) - TSocketMultiplexerMethodJob(T* object, Method method, - ArchSocket socket, bool readable, bool writeable); - virtual ~TSocketMultiplexerMethodJob(); + TSocketMultiplexerMethodJob(const RunFunction& func, + ArchSocket socket, bool readable, bool writable) : + func_{func}, + m_socket(ARCH->copySocket(socket)), + m_readable(readable), + m_writable(writable) + { + } + + ~TSocketMultiplexerMethodJob() override + { + ARCH->closeSocket(m_socket); + } // IJob overrides - virtual MultiplexerJobStatus run(bool readable, bool writable, bool error) override; - virtual ArchSocket getSocket() const override; - virtual bool isReadable() const override; - virtual bool isWritable() const override; + virtual MultiplexerJobStatus run(bool readable, bool writable, bool error) override + { + if (func_) { + return func_(this, readable, writable, error); + } + return {false, {}}; + } + + virtual ArchSocket getSocket() const override { return m_socket; } + virtual bool isReadable() const override { return m_readable; } + virtual bool isWritable() const override { return m_writable; } private: - T* m_object; - Method m_method; + RunFunction func_; ArchSocket m_socket; bool m_readable; bool m_writable; - void* m_arg; }; -template <class T> -inline -TSocketMultiplexerMethodJob<T>::TSocketMultiplexerMethodJob(T* object, - Method method, ArchSocket socket, - bool readable, bool writable) : - m_object(object), - m_method(method), - m_socket(ARCH->copySocket(socket)), - m_readable(readable), - m_writable(writable) -{ - // do nothing -} - -template <class T> -inline -TSocketMultiplexerMethodJob<T>::~TSocketMultiplexerMethodJob() -{ - ARCH->closeSocket(m_socket); -} - -template <class T> -inline MultiplexerJobStatus TSocketMultiplexerMethodJob<T>::run(bool read, bool write, bool error) -{ - if (m_object != NULL) { - return (m_object->*m_method)(this, read, write, error); - } - return {false, {}}; -} - -template <class T> -inline -ArchSocket -TSocketMultiplexerMethodJob<T>::getSocket() const -{ - return m_socket; -} - -template <class T> -inline -bool -TSocketMultiplexerMethodJob<T>::isReadable() const -{ - return m_readable; -} -template <class T> -inline -bool -TSocketMultiplexerMethodJob<T>::isWritable() const -{ - return m_writable; -} diff --git a/src/lib/net/XSocket.cpp b/src/lib/net/XSocket.cpp index eed7a26..6a50537 100644 --- a/src/lib/net/XSocket.cpp +++ b/src/lib/net/XSocket.cpp @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 @@ -63,7 +63,7 @@ std::string XSocketAddress::getWhat() const noexcept "invalid port" // m_port may not be set to the bad port }; return format(s_errorID[m_error], s_errorMsg[m_error], - m_hostname.c_str(), + m_hostname.c_str(), barrier::string::sprintf("%d", m_port).c_str()); } diff --git a/src/lib/net/XSocket.h b/src/lib/net/XSocket.h index d12278e..cafe59c 100644 --- a/src/lib/net/XSocket.h +++ b/src/lib/net/XSocket.h @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package 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 |
