From c06a20d093536c16019add4f9bd01c1eee164d18 Mon Sep 17 00:00:00 2001 From: Serhii Hadzhilov <71632867+SerhiiGadzhilov@users.noreply.github.com> Date: Thu, 29 Sep 2022 11:35:27 +0300 Subject: [PATCH] Initiate TLS connection from server (#7225) * Add SecureClientSocket * Add SecureServerSocket * SecureServerSocket code cleanup * Refactoring for SecureClientSocket * Change base class for SecureClientSocket to InverseClientSocket * Change base class for SecureServerSocket to InverseServerSocket * Small refactoring * Fix Linux compilation * Fix code smells * Refactoring * Update ChangeLog * Fix code smells Co-authored-by: Serhii Hadzhilov --- ChangeLog | 1 + .../net/InverseSockets/InverseClientSocket.h | 1 + .../InverseSockets/InverseServerSocket.cpp | 2 +- .../net/InverseSockets/InverseServerSocket.h | 2 +- .../InverseSockets/InverseSocketFactory.cpp | 9 +- .../net/InverseSockets/SecureClientSocket.cpp | 491 ++++++++++++++++++ .../net/InverseSockets/SecureClientSocket.h | 71 +++ .../net/InverseSockets/SecureServerSocket.cpp | 86 +++ .../net/InverseSockets/SecureServerSocket.h | 30 ++ src/lib/net/InverseSockets/SslApi.cpp | 260 ++++++++++ src/lib/net/InverseSockets/SslApi.h | 56 ++ src/lib/net/InverseSockets/SslLogger.cpp | 187 +++++++ src/lib/net/InverseSockets/SslLogger.h | 29 ++ src/lib/net/SecureSocket.cpp | 166 +----- src/lib/net/SecureSocket.h | 8 +- 15 files changed, 1235 insertions(+), 164 deletions(-) create mode 100644 src/lib/net/InverseSockets/SecureClientSocket.cpp create mode 100644 src/lib/net/InverseSockets/SecureClientSocket.h create mode 100644 src/lib/net/InverseSockets/SecureServerSocket.cpp create mode 100644 src/lib/net/InverseSockets/SecureServerSocket.h create mode 100644 src/lib/net/InverseSockets/SslApi.cpp create mode 100644 src/lib/net/InverseSockets/SslApi.h create mode 100644 src/lib/net/InverseSockets/SslLogger.cpp create mode 100644 src/lib/net/InverseSockets/SslLogger.h diff --git a/ChangeLog b/ChangeLog index 531bf19df..ea738805b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,7 @@ Enhancements: - #7222 Ability to initiate connection from server +- #7225 Ability to initiate TLS connection from server 1.14.5 ====== diff --git a/src/lib/net/InverseSockets/InverseClientSocket.h b/src/lib/net/InverseSockets/InverseClientSocket.h index 79eaa5648..458f9c7ab 100644 --- a/src/lib/net/InverseSockets/InverseClientSocket.h +++ b/src/lib/net/InverseSockets/InverseClientSocket.h @@ -97,6 +97,7 @@ private: serviceConnected(ISocketMultiplexerJob*, bool, bool, bool); +protected: bool m_readable = false; bool m_writable = false; bool m_connected = false; diff --git a/src/lib/net/InverseSockets/InverseServerSocket.cpp b/src/lib/net/InverseSockets/InverseServerSocket.cpp index 07e7cdc04..ba07e27a6 100644 --- a/src/lib/net/InverseSockets/InverseServerSocket.cpp +++ b/src/lib/net/InverseSockets/InverseServerSocket.cpp @@ -87,7 +87,7 @@ InverseServerSocket::accept() } return nullptr; } - catch (const std::exception &ex) { + catch (const std::exception&) { if (socket != nullptr) { delete socket; setListeningJob(); diff --git a/src/lib/net/InverseSockets/InverseServerSocket.h b/src/lib/net/InverseSockets/InverseServerSocket.h index 3f1866371..6a8febfaf 100644 --- a/src/lib/net/InverseSockets/InverseServerSocket.h +++ b/src/lib/net/InverseSockets/InverseServerSocket.h @@ -53,7 +53,7 @@ public: serviceListening(ISocketMultiplexerJob*, bool, bool, bool); -private: +protected: AutoArchSocket m_socket; Mutex m_mutex; IEventQueue* m_events; diff --git a/src/lib/net/InverseSockets/InverseSocketFactory.cpp b/src/lib/net/InverseSockets/InverseSocketFactory.cpp index 171c815fa..2c2f54fca 100644 --- a/src/lib/net/InverseSockets/InverseSocketFactory.cpp +++ b/src/lib/net/InverseSockets/InverseSocketFactory.cpp @@ -17,8 +17,8 @@ #include "InverseSocketFactory.h" #include "net/InverseSockets/InverseClientSocket.h" #include "net/InverseSockets/InverseServerSocket.h" -#include "net/SecureSocket.h" -#include "net/SecureListenSocket.h" +#include "net/InverseSockets/SecureClientSocket.h" +#include "net/InverseSockets/SecureServerSocket.h" // // InverseSocketFactory @@ -34,8 +34,7 @@ IDataSocket* InverseSocketFactory::create(bool secure, IArchNetwork::EAddressFamily family) const { if (secure) { - auto secureSocket = new SecureSocket(m_events, m_socketMultiplexer, family); - secureSocket->initSsl (false); + auto secureSocket = new SecureClientSocket(m_events, m_socketMultiplexer, family); return secureSocket; } else { @@ -49,7 +48,7 @@ InverseSocketFactory::createListen(bool secure, IArchNetwork::EAddressFamily fam IListenSocket* socket = nullptr; if (secure) { - socket = new SecureListenSocket(m_events, m_socketMultiplexer, family); + socket = new SecureServerSocket(m_events, m_socketMultiplexer, family); } else { socket = new InverseServerSocket(m_events, m_socketMultiplexer, family); diff --git a/src/lib/net/InverseSockets/SecureClientSocket.cpp b/src/lib/net/InverseSockets/SecureClientSocket.cpp new file mode 100644 index 000000000..a02b3206c --- /dev/null +++ b/src/lib/net/InverseSockets/SecureClientSocket.cpp @@ -0,0 +1,491 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015-2022 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 + * 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 . + */ + +#include "SecureClientSocket.h" +#include "SslLogger.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +// +// SecureClientSocket +// +constexpr float s_retryDelay = 0.01f; + +SecureClientSocket::SecureClientSocket(IEventQueue* events, + SocketMultiplexer* socketMultiplexer, IArchNetwork::EAddressFamily family) : + InverseClientSocket(events, socketMultiplexer, family) +{ +} + +void +SecureClientSocket::connect(const NetworkAddress& addr) +{ + m_events->adoptHandler(m_events->forIDataSocket().connected(), + getEventTarget(), + new TMethodEventJob(this, + &SecureClientSocket::handleTCPConnected)); + + InverseClientSocket::connect(addr); +} + +ISocketMultiplexerJob* +SecureClientSocket::newJob() +{ + // after TCP connection is established, SecureClientSocket will pick up + // connected event and do secureConnect + if (m_connected && !m_secureReady) { + return nullptr; + } + + return InverseClientSocket::newJob(getSocket()); +} + + +void +SecureClientSocket::secureConnect() +{ + setJob(new TSocketMultiplexerMethodJob( + this, &SecureClientSocket::serviceConnect, + getSocket(), isReadable(), isWritable())); +} + +void +SecureClientSocket::secureAccept() +{ + setJob(new TSocketMultiplexerMethodJob( + this, &SecureClientSocket::serviceAccept, + getSocket(), isReadable(), isWritable())); +} + +InverseClientSocket::EJobResult +SecureClientSocket::doRead() +{ + UInt8 buffer[4096] = {0}; + int bytesRead = 0; + int status = 0; + + if (isSecureReady()) { + status = secureRead(buffer, sizeof(buffer), bytesRead); + + if (status < 0) { + return InverseClientSocket::EJobResult::kBreak; + } + else if (status == 0) { + return InverseClientSocket::EJobResult::kNew; + } + } + else { + return InverseClientSocket::EJobResult::kRetry; + } + + if (bytesRead > 0) { + bool wasEmpty = (m_inputBuffer.getSize() == 0); + + // slurp up as much as possible + do { + m_inputBuffer.write(buffer, bytesRead); + + status = secureRead(buffer, sizeof(buffer), bytesRead); + if (status < 0) { + return InverseClientSocket::EJobResult::kBreak; + } + } while (bytesRead > 0 || status > 0); + + // send input ready if input buffer was empty + if (wasEmpty) { + sendEvent(m_events->forIStream().inputReady()); + } + } + else { + // remote write end of stream hungup. our input side + // has therefore shutdown but don't flush our buffer + // since there's still data to be read. + sendEvent(m_events->forIStream().inputShutdown()); + if (!m_writable && m_inputBuffer.getSize() == 0) { + sendEvent(m_events->forISocket().disconnected()); + m_connected = false; + } + m_readable = false; + return InverseClientSocket::EJobResult::kNew; + } + + return InverseClientSocket::EJobResult::kRetry; +} + +InverseClientSocket::EJobResult +SecureClientSocket::doWrite() +{ + static bool s_retry = false; + static int s_retrySize = 0; + static int s_staticBufferSize = 0; + static void* s_staticBuffer = nullptr; + + // write data + int bufferSize = 0; + int bytesWrote = 0; + int status = 0; + + if (s_retry) { + bufferSize = s_retrySize; + } + else { + bufferSize = m_outputBuffer.getSize(); + if (bufferSize != 0) { + if (bufferSize > s_staticBufferSize) { + s_staticBuffer = realloc(s_staticBuffer, bufferSize); + s_staticBufferSize = bufferSize; + } + memcpy(s_staticBuffer, m_outputBuffer.peek(bufferSize), bufferSize); + } + } + + if (bufferSize == 0) { + return InverseClientSocket::EJobResult::kRetry; + } + + if (isSecureReady()) { + status = secureWrite(s_staticBuffer, bufferSize, bytesWrote); + if (status > 0) { + s_retry = false; + } + else if (status < 0) { + return InverseClientSocket::EJobResult::kBreak; + } + else if (status == 0) { + s_retry = true; + s_retrySize = bufferSize; + return InverseClientSocket::EJobResult::kNew; + } + } + else { + return InverseClientSocket::EJobResult::kRetry; + } + + if (bytesWrote > 0) { + discardWrittenData(bytesWrote); + return InverseClientSocket::EJobResult::kNew; + } + + return InverseClientSocket::EJobResult::kRetry; +} + +int +SecureClientSocket::secureRead(void* buffer, int size, int& read) +{ + LOG((CLOG_DEBUG2 "reading secure socket")); + read = m_ssl.read(static_cast(buffer), size); + + static int retry = 0; + + // Check result will cleanup the connection in the case of a fatal + checkResult(read, retry); + + if (retry) { + return 0; + } + + if (isFatal()) { + return -1; + } + // According to SSL spec, the number of bytes read must not be negative and + // not have an error code from SSL_get_error(). If this happens, it is + // itself an error. Let the parent handle the case + return read; +} + +int +SecureClientSocket::secureWrite(const void* buffer, int size, int& wrote) +{ + LOG((CLOG_DEBUG2 "writing secure socket: %p", this)); + wrote = m_ssl.write(static_cast(buffer), size); + + static int retry = 0; + + // Check result will cleanup the connection in the case of a fatal + checkResult(wrote, retry); + + if (retry) { + return 0; + } + + if (isFatal()) { + return -1; + } + + // According to SSL spec, r must not be negative and not have an error code + // from SSL_get_error(). If this happens, it is itself an error. Let the + // parent handle the case + return wrote; +} + +bool +SecureClientSocket::isSecureReady() const +{ + return m_secureReady; +} + +bool +SecureClientSocket::loadCertificates(const std::string& filename) +{ + return m_ssl.loadCertificate(filename); +} + +int +SecureClientSocket::secureAccept(int socket) +{ + LOG((CLOG_DEBUG2 "accepting secure socket")); + static int retry = 0; + checkResult(m_ssl.accept(socket), retry); + + if (isFatal()) { + // tell user and sleep so the socket isn't hammered. + LOG((CLOG_ERR "failed to accept secure socket")); + LOG((CLOG_WARN "client connection may not be secure")); + m_secureReady = false; + ARCH->sleep(1); + retry = 0; + return -1; // Failed, error out + } + + // If not fatal and no retry, state is good + if (retry == 0) { + m_secureReady = true; + LOG((CLOG_INFO "accepted secure socket")); + m_ssl.logSecureInfo(); + return 1; + } + + // If not fatal and retry is set, not ready, and return retry + if (retry > 0) { + LOG((CLOG_DEBUG2 "retry accepting secure socket")); + m_secureReady = false; + ARCH->sleep(s_retryDelay); + return 0; + } + + // no good state exists here + LOG((CLOG_ERR "unexpected state attempting to accept connection")); + return -1; +} + +int +SecureClientSocket::secureConnect(int socket) +{ + LOG((CLOG_DEBUG2 "connecting secure socket")); + static int retry = 0; + checkResult(m_ssl.connect(socket), retry); + + if (isFatal()) { + LOG((CLOG_ERR "failed to connect secure socket")); + retry = 0; + return -1; + } + + // If we should retry, not ready and return 0 + if (retry > 0) { + LOG((CLOG_DEBUG2 "retry connect secure socket")); + m_secureReady = false; + ARCH->sleep(s_retryDelay); + return 0; + } + + retry = 0; + // No error, set ready, process and return ok + m_secureReady = true; + + auto fingerprint = m_ssl.getFingerprint(); + LOG((CLOG_NOTE "server fingerprint: %s", fingerprint.c_str())); + + if (m_ssl.isTrustedFingerprint(fingerprint)) { + LOG((CLOG_INFO "connected to secure socket")); + m_ssl.logSecureInfo(); + return 1; + } + else { + LOG((CLOG_ERR "failed to verify server certificate fingerprint")); + disconnect(); + return -1; // Fingerprint failed, error + } +} + +void SecureClientSocket::setFatal(int code) +{ + const std::set nonFatal { + SSL_ERROR_NONE, + SSL_ERROR_WANT_READ, + SSL_ERROR_WANT_WRITE, + SSL_ERROR_WANT_CONNECT, + SSL_ERROR_WANT_ACCEPT + }; + m_fatal = nonFatal.find(code) == nonFatal.end(); +} + +int SecureClientSocket::getRetry(int errorCode, int retry) const +{ + const std::set retryCodes { + SSL_ERROR_WANT_READ, + SSL_ERROR_WANT_WRITE, + SSL_ERROR_WANT_CONNECT, + SSL_ERROR_WANT_ACCEPT + }; + + if (errorCode == SSL_ERROR_NONE || isFatal()) { + retry = 0; + } + else if (retryCodes.find(errorCode) != retryCodes.end()) { + ++retry; + } + + return retry; +} + +void +SecureClientSocket::checkResult(int status, int& retry) +{ + // ssl errors are a little quirky. the "want" errors are normal and + // should result in a retry. + int errorCode = m_ssl.getErrorCode(status); + setFatal(errorCode); + retry = getRetry(errorCode, retry); + + switch (errorCode) { + 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 + // m_readable because the socket logic is always readable + m_writable = true; + break; + + case SSL_ERROR_SYSCALL: + if (ERR_peek_error() == 0) { + if (status == 0) { + LOG((CLOG_ERR "eof violates tls protocol")); + } + else if (status == -1) { + // underlying socket I/O reproted an error + try { + ARCH->throwErrorOnSocket(getSocket()); + } + catch (const XArchNetwork& e) { + LOG((CLOG_ERR "%s", e.what())); + } + } + } + break; + default: + break; + } + + SslLogger::logErrorByCode(errorCode, retry); + + if (isFatal()) { + SslLogger::logError(); + disconnect(); + } +} + +void +SecureClientSocket::disconnect() +{ + sendEvent(getEvents()->forISocket().stopRetry()); + sendEvent(getEvents()->forISocket().disconnected()); + sendEvent(getEvents()->forIStream().inputShutdown()); +} + +ISocketMultiplexerJob* +SecureClientSocket::serviceConnect(ISocketMultiplexerJob*, bool, bool, bool) +{ + Lock lock(&getMutex()); + + int status = 0; + +#ifdef SYSAPI_WIN32 + status = secureConnect(static_cast(getSocket()->m_socket)); +#elif SYSAPI_UNIX + status = secureConnect(getSocket()->m_fd); +#endif + + // If status < 0, error happened + if (status < 0) { + return nullptr; + } + + // If status > 0, success + if (status > 0) { + sendEvent(m_events->forIDataSocket().secureConnected()); + return newJob(); + } + + // Retry case + return new TSocketMultiplexerMethodJob( + this, &SecureClientSocket::serviceConnect, + getSocket(), isReadable(), isWritable()); +} + +ISocketMultiplexerJob* +SecureClientSocket::serviceAccept(ISocketMultiplexerJob*, bool, bool, bool) +{ + Lock lock(&getMutex()); + + int status = 0; +#ifdef SYSAPI_WIN32 + status = secureAccept(static_cast(getSocket()->m_socket)); +#elif SYSAPI_UNIX + status = secureAccept(getSocket()->m_fd); +#endif + // If status < 0, error happened + if (status < 0) { + return nullptr; + } + + // If status > 0, success + if (status > 0) { + sendEvent(m_events->forClientListener().accepted()); + return newJob(); + } + + // Retry case + return new TSocketMultiplexerMethodJob( + this, &SecureClientSocket::serviceAccept, + getSocket(), isReadable(), isWritable()); +} + +void +SecureClientSocket::handleTCPConnected(const Event&, void*) +{ + if (getSocket()) { + secureConnect(); + } + else { + LOG((CLOG_DEBUG "disregarding stale connect event")); + } +} diff --git a/src/lib/net/InverseSockets/SecureClientSocket.h b/src/lib/net/InverseSockets/SecureClientSocket.h new file mode 100644 index 000000000..8bf1ffb4b --- /dev/null +++ b/src/lib/net/InverseSockets/SecureClientSocket.h @@ -0,0 +1,71 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015-2022 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 + * 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 . + */ +#pragma once +#include "SslApi.h" +#include "InverseClientSocket.h" + +//! Secure socket +/*! +A secure socket using SSL. +*/ +class SecureClientSocket : public InverseClientSocket { +public: + SecureClientSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, IArchNetwork::EAddressFamily family); + SecureClientSocket(SecureClientSocket const &) =delete; + SecureClientSocket(SecureClientSocket &&) =delete; + + SecureClientSocket& operator=(SecureClientSocket const &) =delete; + SecureClientSocket& operator=(SecureClientSocket &&) =delete; + + // IDataSocket overrides + void connect(const NetworkAddress&) override; + + ISocketMultiplexerJob* newJob(); + bool isFatal() const { return m_fatal; } + void setFatal(int code); + int getRetry(int errorCode, int retry) const; + bool isSecureReady() const; + void secureConnect(); + void secureAccept(); + int secureRead(void* buffer, int size, int& read); + int secureWrite(const void* buffer, int size, int& wrote); + EJobResult doRead() override; + EJobResult doWrite() override; + bool loadCertificates(const std::string& CertFile); + +private: + // SSL + void initContext(bool server); + int secureAccept(int s); + int secureConnect(int s); + void checkResult(int n, int& retry); + void disconnect(); + + ISocketMultiplexerJob* + serviceConnect(ISocketMultiplexerJob*, + bool, bool, bool); + + ISocketMultiplexerJob* + serviceAccept(ISocketMultiplexerJob*, + bool, bool, bool); + + void handleTCPConnected(const Event&, void*); + + synergy::ssl::SslApi m_ssl{false}; + bool m_secureReady = false; + bool m_fatal = false; +}; diff --git a/src/lib/net/InverseSockets/SecureServerSocket.cpp b/src/lib/net/InverseSockets/SecureServerSocket.cpp new file mode 100644 index 000000000..d62b97ce5 --- /dev/null +++ b/src/lib/net/InverseSockets/SecureServerSocket.cpp @@ -0,0 +1,86 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015-2022 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 + * 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 . + */ + +#include "SecureServerSocket.h" + +#include +#include +#include +#include +#include + +// +// SecureServerSocket +// +SecureServerSocket::SecureServerSocket( + IEventQueue* events, + SocketMultiplexer* socketMultiplexer, + IArchNetwork::EAddressFamily family) : + InverseServerSocket(events, socketMultiplexer, family) +{ +} + +IDataSocket* +SecureServerSocket::accept() +{ + SecureSocket* socket = nullptr; + + try { + socket = new SecureSocket(m_events, m_socketMultiplexer, m_socket.getRawSocket()); + socket->initSsl(true); + setListeningJob(); + + auto certificateFilename = getCertifcateFileName(); + if (socket->loadCertificates(certificateFilename)) { + socket->secureAccept(); + } + else { + delete socket; + socket = nullptr; + } + } + catch (const XArchNetwork&) { + if (socket) { + delete socket; + socket = nullptr; + setListeningJob(); + } + } + catch (const std::exception&) { + if (socket) { + delete socket; + setListeningJob(); + } + throw; + } + + return dynamic_cast(socket); +} + +std::string +SecureServerSocket::getCertifcateFileName() const +{ + //if the tls cert option is set use that for the certificate file + auto certificateFilename = ArgParser::argsBase().m_tlsCertFile; + + if (certificateFilename.empty()) { + //default location of the TLS cert file in users dir + certificateFilename = synergy::string::sprintf("%s/SSL/Synergy.pem", ARCH->getProfileDirectory().c_str()); + } + + return certificateFilename; +} diff --git a/src/lib/net/InverseSockets/SecureServerSocket.h b/src/lib/net/InverseSockets/SecureServerSocket.h new file mode 100644 index 000000000..6e808f310 --- /dev/null +++ b/src/lib/net/InverseSockets/SecureServerSocket.h @@ -0,0 +1,30 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015-2022 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 + * 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 . + */ +#pragma once +#include "InverseServerSocket.h" + +class SecureServerSocket : public InverseServerSocket { +public: + SecureServerSocket(IEventQueue* events, + SocketMultiplexer* socketMultiplexer, IArchNetwork::EAddressFamily family); + + // IListenSocket overrides + IDataSocket* accept() override; + +private: + std::string getCertifcateFileName() const; +}; diff --git a/src/lib/net/InverseSockets/SslApi.cpp b/src/lib/net/InverseSockets/SslApi.cpp new file mode 100644 index 000000000..c03e232ca --- /dev/null +++ b/src/lib/net/InverseSockets/SslApi.cpp @@ -0,0 +1,260 @@ +#include "SslApi.h" +#include "SslLogger.h" + +#include +#include + +#include +#include +#include + +namespace synergy { +namespace ssl { + +using AutoX509 = std::unique_ptr; + +SslApi::SslApi(bool isServer) +{ + SSL_library_init(); + // load & register all cryptos, etc. + OpenSSL_add_all_algorithms(); + // load all error messages + SSL_load_error_strings(); + createContext(isServer); + SslLogger::logSecureLibInfo(); +} + +SslApi::~SslApi() +{ + if (m_ssl) { + SSL_shutdown(m_ssl); + SSL_free(m_ssl); + m_ssl = nullptr; + } + + if (m_context) { + SSL_CTX_free(m_context); + m_context = nullptr; + } +} + +int SslApi::read(char* buffer, int size) +{ + auto read = 0; + + if (m_ssl) { + read = SSL_read(m_ssl, buffer, size); + } + + return read; +} + +int SslApi::write(const char* buffer, int size) +{ + auto wrote = 0; + + if (m_ssl) { + wrote = SSL_write(m_ssl, buffer, size); + } + + return wrote; +} + +int SslApi::accept(int socket) +{ + int result = 0; + + if (m_ssl) { + // set connection socket to SSL state + SSL_set_fd(m_ssl, socket); + result = SSL_accept(m_ssl); + } + + return result; +} + +int SslApi::connect(int socket) +{ + auto result = 0; + + if (m_ssl) { + // attach the socket descriptor + SSL_set_fd(m_ssl, socket); + result = SSL_connect(m_ssl); + } + + return result; +} + +void SslApi::createSSL() +{ + if (m_ssl == nullptr && m_context != nullptr) { + m_ssl = SSL_new(m_context); + } +} + +bool SslApi::loadCertificate(const std::string& filename) +{ + bool result = false; + + if (isCertificateExists(filename)) { + auto r = SSL_CTX_use_certificate_file(m_context, filename.c_str(), SSL_FILETYPE_PEM); + if (r <= 0) { + SslLogger::logError("could not use tls certificate"); + return false; + } + + r = SSL_CTX_use_PrivateKey_file(m_context, filename.c_str(), SSL_FILETYPE_PEM); + if (r <= 0) { + SslLogger::logError("could not use tls private key"); + return false; + } + + r = SSL_CTX_check_private_key(m_context); + if (!r) { + SslLogger::logError("could not verify tls private key"); + return false; + } + } + + return result; +} + +bool SslApi::showCertificate() const +{ + bool result = false; + + if (m_ssl) { + // get the server's certificate + AutoX509 cert(SSL_get_peer_certificate(m_ssl), &X509_free); + if (cert) { + auto line = X509_NAME_oneline(X509_get_subject_name(cert.get()), nullptr, 0); + LOG((CLOG_INFO "server tls certificate info: %s", line)); + OPENSSL_free(line); + result = true; + } + else { + SslLogger::logError("server has no tls certificate"); + } + } + + return result; +} + +std::string SslApi::getFingerprint() const +{ + // calculate received certificate fingerprint + AutoX509 cert(SSL_get_peer_certificate(m_ssl), &X509_free); + unsigned int tempFingerprintLen = 0; + unsigned char tempFingerprint[EVP_MAX_MD_SIZE] = {0}; + int digestResult = X509_digest(cert.get(), EVP_sha256(), tempFingerprint, &tempFingerprintLen); + + if (digestResult <= 0) { + LOG((CLOG_ERR "failed to calculate fingerprint, digest result: %d", digestResult)); + return ""; + } + + // format fingerprint into hexdecimal format with colon separator + std::string fingerprint(static_cast(static_cast(tempFingerprint)), tempFingerprintLen); + formatFingerprint(fingerprint); + + return fingerprint; +} + +bool SslApi::isTrustedFingerprint(const std::string& fingerprint) const +{ + auto trustedServersFilename = synergy::string::sprintf( + "%s/SSL/Fingerprints/TrustedServers.txt", + ARCH->getProfileDirectory().c_str()); + + // check if this fingerprint exist + std::ifstream file; + file.open(synergy::filesystem::path(trustedServersFilename)); + + bool isValid = false; + if (file.is_open()) { + while (!file.eof()) { + std::string fileLine; + getline(file, fileLine); + if (!fileLine.empty() && !fileLine.compare(fingerprint)) { + isValid = true; + break; + } + } + } + else { + LOG((CLOG_ERR "Fail to open trusted fingerprints file: %s", trustedServersFilename.c_str())); + } + + return (isValid && showCertificate()); +} + + +void SslApi::createContext(bool isServer) +{ + // create new context from method + if (isServer) { + m_context = SSL_CTX_new(SSLv23_server_method()); + } + else { + m_context = SSL_CTX_new(SSLv23_client_method()); + } + //Prevent the usage of of all version prior to TLSv1.2 as they are known to be vulnerable + SSL_CTX_set_options(m_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); + + if (m_context) { + m_ssl = SSL_new(m_context); + } + else { + SslLogger::logError(); + } +} + +void SslApi::logSecureInfo() const +{ + SslLogger::logSecureCipherInfo(m_ssl); + SslLogger::logSecureConnectInfo(m_ssl); + +} + +int SslApi::getErrorCode(int status) const +{ + return SSL_get_error(m_ssl, status); +} + +void SslApi::formatFingerprint(std::string &fingerprint) const +{ + // to hexidecimal + synergy::string::toHex(fingerprint, 2); + // all uppercase + synergy::string::uppercase(fingerprint); + // 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 SslApi::isCertificateExists(const std::string &filename) const +{ + bool result = (!filename.empty()); + + if (result) { + std::ifstream file(synergy::filesystem::path(filename)); + result = file.good(); + + if (!result) { + std::string errorMsg("tls certificate doesn't exist: "); + errorMsg.append(filename); + SslLogger::logError(errorMsg.c_str()); + } + } + else { + SslLogger::logError("tls certificate is not specified"); + } + + return result; +} + +} //namespace ssl +} //namespace synergy diff --git a/src/lib/net/InverseSockets/SslApi.h b/src/lib/net/InverseSockets/SslApi.h new file mode 100644 index 000000000..d9110ef25 --- /dev/null +++ b/src/lib/net/InverseSockets/SslApi.h @@ -0,0 +1,56 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015-2022 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 + * 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 . + */ +#pragma once +#include +#include + +namespace synergy { +namespace ssl { + +class SslApi +{ +public: + explicit SslApi(bool isServer = false); + SslApi(SslApi const &) =delete; + SslApi& operator=(SslApi const &) =delete; + ~SslApi(); + + int read(char* buffer, int size); + int write(const char* buffer, int size); + int accept(int socket); + int connect(int socket); + + bool loadCertificate(const std::string& filename); + bool showCertificate() const; + std::string getFingerprint() const; + bool isTrustedFingerprint(const std::string& fingerprint) const; + + void logSecureInfo() const; + int getErrorCode(int status) const; + +private: + void createSSL(); + void formatFingerprint(std::string& fingerprint) const; + bool isCertificateExists(const std::string& filename) const; + void createContext(bool isServer = false); + + SSL* m_ssl = nullptr; + SSL_CTX* m_context = nullptr; +}; + +} //namespace ssl +} //namespace synergy diff --git a/src/lib/net/InverseSockets/SslLogger.cpp b/src/lib/net/InverseSockets/SslLogger.cpp new file mode 100644 index 000000000..23f9ac340 --- /dev/null +++ b/src/lib/net/InverseSockets/SslLogger.cpp @@ -0,0 +1,187 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015-2022 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 + * 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 . + */ +#include "SslLogger.h" +#include +#include + +#include +#include +#include + +namespace { + +void showCipherStackDesc(STACK_OF(SSL_CIPHER)* stack) +{ + char msg[128] = {0}; + for (int i = 0; i < sk_SSL_CIPHER_num(stack); ++i) { + auto cipher = sk_SSL_CIPHER_value(stack, i); + SSL_CIPHER_description(cipher, msg, sizeof(msg)); + + // SSL puts a newline in the description + auto pos = strnlen(msg, sizeof(msg)) - 1; + if (msg[pos] == '\n') { + msg[pos] = '\0'; + } + + LOG((CLOG_DEBUG1 "%s", msg)); + } +} + +void logLocalSecureCipherInfo(const SSL* ssl) +{ + auto sStack = SSL_get_ciphers(ssl); + + if (sStack) { + LOG((CLOG_DEBUG1 "available local ciphers:")); + showCipherStackDesc(sStack); + } + else { + LOG((CLOG_DEBUG1 "local cipher list not available")); + } +} + +void logRemoteSecureCipherInfo(const SSL* ssl) +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + // ssl->session->ciphers is not forward compatable, + // In future release of OpenSSL, it's not visible, + // however, LibreSSL still uses this. + auto cStack = ssl->session->ciphers; +#else + // Use SSL_get_client_ciphers() for newer versions of OpenSSL. + auto cStack = SSL_get_client_ciphers(ssl); +#endif + if (cStack) { + LOG((CLOG_DEBUG1 "available remote ciphers:")); + showCipherStackDesc(cStack); + } + else { + LOG((CLOG_DEBUG1 "remote cipher list not available")); + } +} + +}// namespace + +void SslLogger::logSecureLibInfo() +{ + if (CLOG->getFilter() >= kDEBUG) { + LOG((CLOG_DEBUG "openssl version: %s", SSLeay_version(SSLEAY_VERSION))); + LOG((CLOG_DEBUG1 "openssl flags: %s", SSLeay_version(SSLEAY_CFLAGS))); + LOG((CLOG_DEBUG1 "openssl built on: %s", SSLeay_version(SSLEAY_BUILT_ON))); + LOG((CLOG_DEBUG1 "openssl platform: %s", SSLeay_version(SSLEAY_PLATFORM))); + LOG((CLOG_DEBUG1 "openssl dir: %s", SSLeay_version(SSLEAY_DIR))); + } +} + +void SslLogger::logSecureCipherInfo(const SSL* ssl) +{ + if (ssl && CLOG->getFilter() >= kDEBUG1) { + logLocalSecureCipherInfo(ssl); + logRemoteSecureCipherInfo(ssl); + } +} + +void SslLogger::logSecureConnectInfo(const SSL* ssl) +{ + if (ssl) { + auto cipher = SSL_get_current_cipher(ssl); + + if (cipher) { + char msg[128] = {0}; + SSL_CIPHER_description(cipher, msg, sizeof(msg)); + LOG((CLOG_DEBUG "openssl cipher: %s", msg)); + + //For some reason SSL_get_version is return mismatching information to SSL_CIPHER_description + // so grab the version out the description instead, This seems like a hacky way of doing it. + // But when the cipher says "TLSv1.2" but the get_version returns "TLSv1/SSLv3" we it doesn't look right + // For some reason macOS hates regex's so stringstream is used + std::istringstream iss(msg); + + //Take the stream input and splits it into a vetor directly + const std::vector parts{std::istream_iterator{iss}, + std::istream_iterator{}}; + if (parts.size() > 2) + { + //log the section containing the protocol version + LOG((CLOG_INFO "network encryption protocol: %s", parts[1].c_str())); + } + else + { + //log the error in spliting then display the whole description rather then nothing + LOG((CLOG_ERR "could not split cipher for protocol")); + LOG((CLOG_INFO "network encryption protocol: %s", msg)); + } + } + else { + LOG((CLOG_ERR "could not get secure socket cipher")); + } + } +} + +void SslLogger::logError(const std::string &reason) +{ + if (!reason.empty()) { + LOG((CLOG_ERR "secure socket error: %s", reason.c_str())); + } + + auto id = ERR_get_error(); + if (id) { + char error[65535] = {0}; + ERR_error_string_n(id, error, sizeof(error)); + LOG((CLOG_ERR "openssl error: %s", error)); + } +} + +void SslLogger::logErrorByCode(int code, int retry) +{ + switch (code) { + case SSL_ERROR_NONE: + break; + + case SSL_ERROR_ZERO_RETURN: + LOG((CLOG_DEBUG "tls connection closed")); + break; + + case SSL_ERROR_WANT_READ: + LOG((CLOG_DEBUG2 "want to read, error=%d, attempt=%d", code, retry)); + break; + + case SSL_ERROR_WANT_WRITE: + LOG((CLOG_DEBUG2 "want to write, error=%d, attempt=%d", code, retry)); + break; + + case SSL_ERROR_WANT_CONNECT: + LOG((CLOG_DEBUG2 "want to connect, error=%d, attempt=%d", code, retry)); + break; + + case SSL_ERROR_WANT_ACCEPT: + LOG((CLOG_DEBUG2 "want to accept, error=%d, attempt=%d", code, retry)); + break; + + case SSL_ERROR_SYSCALL: + LOG((CLOG_ERR "tls error occurred (system call failure)")); + break; + + case SSL_ERROR_SSL: + LOG((CLOG_ERR "tls error occurred (generic failure)")); + break; + + default: + LOG((CLOG_ERR "tls error occurred (unknown failure)")); + break; + } +} diff --git a/src/lib/net/InverseSockets/SslLogger.h b/src/lib/net/InverseSockets/SslLogger.h new file mode 100644 index 000000000..328bc8d0f --- /dev/null +++ b/src/lib/net/InverseSockets/SslLogger.h @@ -0,0 +1,29 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015-2022 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 + * 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 . + */ +#pragma once +#include +#include + +class SslLogger +{ +public: + static void logSecureLibInfo(); + static void logSecureCipherInfo(const SSL* ssl); + static void logSecureConnectInfo(const SSL* ssl); + static void logError(const std::string& reason = ""); + static void logErrorByCode(int code, int retry); +}; diff --git a/src/lib/net/SecureSocket.cpp b/src/lib/net/SecureSocket.cpp index a452470ab..f194be300 100644 --- a/src/lib/net/SecureSocket.cpp +++ b/src/lib/net/SecureSocket.cpp @@ -20,6 +20,7 @@ #include "net/TSocketMultiplexerMethodJob.h" #include "base/TMethodEventJob.h" #include "net/TCPSocket.h" +#include #include "mt/Lock.h" #include "arch/XArch.h" #include "base/Log.h" @@ -312,7 +313,7 @@ bool SecureSocket::loadCertificates(String& filename) { if (filename.empty()) { - showError("tls certificate is not specified"); + SslLogger::logError("tls certificate is not specified"); return false; } else { @@ -323,7 +324,7 @@ SecureSocket::loadCertificates(String& filename) if (!exist) { String errorMsg("tls certificate doesn't exist: "); errorMsg.append(filename); - showError(errorMsg.c_str()); + SslLogger::logError(errorMsg.c_str()); return false; } } @@ -331,19 +332,19 @@ SecureSocket::loadCertificates(String& filename) int r = 0; r = SSL_CTX_use_certificate_file(m_ssl->m_context, filename.c_str(), SSL_FILETYPE_PEM); if (r <= 0) { - showError("could not use tls certificate"); + SslLogger::logError("could not use tls certificate"); return false; } r = SSL_CTX_use_PrivateKey_file(m_ssl->m_context, filename.c_str(), SSL_FILETYPE_PEM); if (r <= 0) { - showError("could not use tls private key"); + SslLogger::logError("could not use tls private key"); return false; } r = SSL_CTX_check_private_key(m_ssl->m_context); if (!r) { - showError("could not verify tls private key"); + SslLogger::logError("could not verify tls private key"); return false; } @@ -362,10 +363,7 @@ SecureSocket::initContext(bool server) // load all error messages SSL_load_error_strings(); - - if (CLOG->getFilter() >= kINFO) { - showSecureLibInfo(); - } + SslLogger::logSecureLibInfo(); if (server) { method = SSLv23_server_method(); @@ -382,7 +380,7 @@ SecureSocket::initContext(bool server) SSL_CTX_set_options(m_ssl->m_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); if (m_ssl->m_context == NULL) { - showError(); + SslLogger::logError(); } } @@ -447,10 +445,8 @@ SecureSocket::secureAccept(int socket) if (retry == 0) { m_secureReady = true; LOG((CLOG_INFO "accepted secure socket")); - if (CLOG->getFilter() >= kDEBUG1) { - showSecureCipherInfo(); - } - showSecureConnectInfo(); + SslLogger::logSecureCipherInfo(m_ssl->m_ssl); + SslLogger::logSecureConnectInfo(m_ssl->m_ssl); return 1; } @@ -512,15 +508,13 @@ SecureSocket::secureConnect(int socket) return -1; // Fingerprint failed, error } LOG((CLOG_DEBUG2 "connected secure socket")); - if (CLOG->getFilter() >= kDEBUG1) { - showSecureCipherInfo(); - } - showSecureConnectInfo(); + SslLogger::logSecureCipherInfo(m_ssl->m_ssl); + SslLogger::logSecureConnectInfo(m_ssl->m_ssl); return 1; } bool -SecureSocket::showCertificate() +SecureSocket::showCertificate() const { X509* cert; char* line; @@ -534,7 +528,7 @@ SecureSocket::showCertificate() X509_free(cert); } else { - showError("server has no tls certificate"); + SslLogger::logError("server has no tls certificate"); return false; } @@ -618,39 +612,11 @@ SecureSocket::checkResult(int status, int& retry) if (isFatal()) { retry = 0; - showError(); + SslLogger::logError(); disconnect(); } } -void -SecureSocket::showError(const char* reason) -{ - if (reason != NULL) { - LOG((CLOG_ERR "secure socket error: %s", reason)); - } - - String error = getError(); - if (!error.empty()) { - LOG((CLOG_ERR "openssl error: %s", error.c_str())); - } -} - -String -SecureSocket::getError() -{ - unsigned long e = ERR_get_error(); - - if (e != 0) { - char error[MAX_ERROR_SIZE]; - ERR_error_string_n(e, error, MAX_ERROR_SIZE); - return error; - } - else { - return ""; - } -} - void SecureSocket::disconnect() { @@ -790,107 +756,7 @@ SecureSocket::serviceAccept(ISocketMultiplexerJob* job, } void -showCipherStackDesc(STACK_OF(SSL_CIPHER) * stack) { - char msg[kMsgSize]; - int i = 0; - for ( ; i < sk_SSL_CIPHER_num(stack) ; i++) { - const SSL_CIPHER * cipher = sk_SSL_CIPHER_value(stack,i); - - SSL_CIPHER_description(cipher, msg, kMsgSize); - - // Why does SSL put a newline in the description? - int pos = (int)strnlen(msg, kMsgSize) - 1; - if (msg[pos] == '\n') { - msg[pos] = '\0'; - } - - LOG((CLOG_DEBUG1 "%s",msg)); - } -} - -void -SecureSocket::showSecureCipherInfo() -{ - STACK_OF(SSL_CIPHER) * sStack = SSL_get_ciphers(m_ssl->m_ssl); - - if (sStack == NULL) { - LOG((CLOG_DEBUG1 "local cipher list not available")); - } - else { - LOG((CLOG_DEBUG1 "available local ciphers:")); - showCipherStackDesc(sStack); - } - -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) - // m_ssl->m_ssl->session->ciphers is not forward compatable, - // In future release of OpenSSL, it's not visible, - // however, LibreSSL still uses this. - STACK_OF(SSL_CIPHER) * cStack = m_ssl->m_ssl->session->ciphers; -#else - // Use SSL_get_client_ciphers() for newer versions of OpenSSL. - STACK_OF(SSL_CIPHER) * cStack = SSL_get_client_ciphers(m_ssl->m_ssl); -#endif - if (cStack == NULL) { - LOG((CLOG_DEBUG1 "remote cipher list not available")); - } - else { - LOG((CLOG_DEBUG1 "available remote ciphers:")); - showCipherStackDesc(cStack); - } - return; -} - -void -SecureSocket::showSecureLibInfo() -{ - LOG((CLOG_DEBUG "openssl version: %s", SSLeay_version(SSLEAY_VERSION))); - LOG((CLOG_DEBUG1 "openssl flags: %s", SSLeay_version(SSLEAY_CFLAGS))); - LOG((CLOG_DEBUG1 "openssl built on: %s", SSLeay_version(SSLEAY_BUILT_ON))); - LOG((CLOG_DEBUG1 "openssl platform: %s", SSLeay_version(SSLEAY_PLATFORM))); - LOG((CLOG_DEBUG1 "openssl dir: %s", SSLeay_version(SSLEAY_DIR))); - return; -} - -void -SecureSocket::showSecureConnectInfo() -{ - const SSL_CIPHER* cipher = SSL_get_current_cipher(m_ssl->m_ssl); - - if (cipher != NULL) { - char msg[kMsgSize]; - SSL_CIPHER_description(cipher, msg, kMsgSize); - LOG((CLOG_DEBUG "openssl cipher: %s", msg)); - - //For some reason SSL_get_version is return mismatching information to SSL_CIPHER_description - // so grab the version out the description instead, This seems like a hacky way of doing it. - // But when the cipher says "TLSv1.2" but the get_version returns "TLSv1/SSLv3" we it doesn't look right - // For some reason macOS hates regex's so stringstream is used - - std::istringstream iss(msg); - - //Take the stream input and splits it into a vetor directly - const std::vector parts{std::istream_iterator{iss}, - std::istream_iterator{}}; - if (parts.size() > 2) - { - //log the section containing the protocol version - LOG((CLOG_INFO "network encryption protocol: %s", parts[1].c_str())); - } - else - { - //log the error in spliting then display the whole description rather then nothing - LOG((CLOG_ERR "could not split cipher for protocol")); - LOG((CLOG_INFO "network encryption protocol: %s", msg)); - } - } - else { - LOG((CLOG_ERR "could not get secure socket cipher")); - } - return; -} - -void -SecureSocket::handleTCPConnected(const Event& event, void*) +SecureSocket::handleTCPConnected(const Event&, void*) { if (getSocket() == nullptr) { LOG((CLOG_DEBUG "disregarding stale connect event")); diff --git a/src/lib/net/SecureSocket.h b/src/lib/net/SecureSocket.h index f31a459ea..1dba8c48e 100644 --- a/src/lib/net/SecureSocket.h +++ b/src/lib/net/SecureSocket.h @@ -70,10 +70,8 @@ private: void freeSSL(); int secureAccept(int s); int secureConnect(int s); - bool showCertificate(); + bool showCertificate() const; void checkResult(int n, int& retry); - void showError(const char* reason = NULL); - String getError(); void disconnect(); void formatFingerprint(String& fingerprint, bool hex = true, @@ -88,10 +86,6 @@ private: serviceAccept(ISocketMultiplexerJob*, bool, bool, bool); - void showSecureConnectInfo(); - void showSecureLibInfo(); - void showSecureCipherInfo(); - void handleTCPConnected(const Event& event, void*); private: