Merge remote-tracking branch 'synergy/master'
This commit is contained in:
commit
abfe8f5b78
|
@ -17,7 +17,7 @@
|
|||
# Version number for Synergy
|
||||
set(VERSION_MAJOR 1)
|
||||
set(VERSION_MINOR 7)
|
||||
set(VERSION_REV 1)
|
||||
set(VERSION_REV 2)
|
||||
set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}")
|
||||
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
|
27
ChangeLog
27
ChangeLog
|
@ -1,3 +1,30 @@
|
|||
v1.7.1
|
||||
======
|
||||
Bug #3784 - Double click & drag doesn't select words on client
|
||||
Bug #3052 - Triple-click (select line) does not work
|
||||
Bug #4367 - Duplicate Alt-S Keyboard Shortcuts on Gui
|
||||
Bug #4554 - Server unable to accept new SSL connection
|
||||
Bug #4553 - SSL handshake failure error causes GUI to crash
|
||||
Bug #4551 - Plugin wizard doesn't create SSL directory
|
||||
Bug #4548 - Severe code duplication in fingerprint logic
|
||||
Bug #4547 - Windows server crashes when client fingerprint dialog open
|
||||
Bug #4539 - Mac client dies when server has SSL_ERROR_SSL
|
||||
Bug #4537 - Plugin wizard doesn't complete but finish button enabled
|
||||
Bug #4535 - Server crashes on shut down after multiple connections failed
|
||||
Bug #4528 - Error SSL_ERROR_SSL is logged on unknown error
|
||||
Bug #4527 - Server fingerprint dialog on client GUI keeps showing
|
||||
Bug #4469 - GUI crashes on Windows when generating certificate
|
||||
Bug #4410 - SSL_ERROR_SSL (unknown protocol) on Mac client
|
||||
Bug #4409 - SSL_ERROR_SSL (unknown alert type) on Windows 8.1 client
|
||||
Bug #4557 - GUI doesn't show local fingerprint on fresh install
|
||||
Enhancement #4522 - SSL server fingerprint verification from client
|
||||
Enhancement #4526 - Display local fingerprint on server GUI
|
||||
Enhancement #4549 - Extract SSL certificate and fingerprint generate function
|
||||
Enhancement #4546 - Redistribute OpenSSL on Windows with installer
|
||||
Enhancement #4540 - Enable Network Security checkbox only when ns plugin exists
|
||||
Enhancement #4525 - Reorganize app data directory
|
||||
Enhancement #4390 - Disable GUI auto-hide by default
|
||||
|
||||
1.7.0
|
||||
=====
|
||||
Enhancement #4313 - SSL encrypted secure connection
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
#
|
||||
# Synergy OpenSSL configuration file.
|
||||
# Used for generation of certificate requests.
|
||||
#
|
||||
|
||||
dir = .
|
||||
|
||||
[ca]
|
||||
default_ca = CA_default
|
||||
|
||||
[CA_default]
|
||||
serial = $dir/serial
|
||||
database = $dir/certindex.txt
|
||||
new_certs_dir = $dir/certs
|
||||
certificate = $dir/cacert.pem
|
||||
private_key = $dir/private/cakey.pem
|
||||
default_days = 365
|
||||
default_md = md5
|
||||
preserve = no
|
||||
email_in_dn = no
|
||||
nameopt = default_ca
|
||||
certopt = default_ca
|
||||
policy = policy_match
|
||||
|
||||
[policy_match]
|
||||
countryName = match
|
||||
stateOrProvinceName = match
|
||||
organizationName = match
|
||||
organizationalUnitName = optional
|
||||
commonName = supplied
|
||||
emailAddress = optional
|
||||
|
||||
[req]
|
||||
default_bits = 1024 # Size of keys
|
||||
default_keyfile = key.pem # name of generated keys
|
||||
default_md = md5 # message digest algorithm
|
||||
string_mask = nombstr # permitted characters
|
||||
distinguished_name = req_distinguished_name
|
||||
req_extensions = v3_req
|
||||
|
||||
[req_distinguished_name]
|
||||
0.organizationName = Organization Name (company)
|
||||
organizationalUnitName = Organizational Unit Name (department, division)
|
||||
emailAddress = Email Address
|
||||
emailAddress_max = 40
|
||||
localityName = Locality Name (city, district)
|
||||
stateOrProvinceName = State or Province Name (full name)
|
||||
countryName = Country Name (2 letter code)
|
||||
countryName_min = 2
|
||||
countryName_max = 2
|
||||
commonName = Common Name (hostname, IP, or your name)
|
||||
commonName_max = 64
|
||||
0.organizationName_default = My Company
|
||||
localityName_default = My Town
|
||||
stateOrProvinceName_default = State or Providence
|
||||
countryName_default = US
|
||||
|
||||
[v3_ca]
|
||||
basicConstraints = CA:TRUE
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always,issuer:always
|
||||
|
||||
[v3_req]
|
||||
basicConstraints = CA:FALSE
|
||||
subjectKeyIdentifier = hash
|
|
@ -55,7 +55,9 @@ SOURCES += src/main.cpp \
|
|||
src/WebClient.cpp \
|
||||
src/PluginWizardPage.cpp \
|
||||
src/PluginManager.cpp \
|
||||
src/CoreInterface.cpp
|
||||
src/CoreInterface.cpp \
|
||||
src/Fingerprint.cpp \
|
||||
src/SslCertificate.cpp
|
||||
HEADERS += src/MainWindow.h \
|
||||
src/AboutDialog.h \
|
||||
src/ServerConfig.h \
|
||||
|
@ -97,7 +99,9 @@ HEADERS += src/MainWindow.h \
|
|||
src/PluginWizardPage.h \
|
||||
src/ProcessorArch.h \
|
||||
src/PluginManager.h \
|
||||
src/CoreInterface.h
|
||||
src/CoreInterface.h \
|
||||
src/Fingerprint.h \
|
||||
src/SslCertificate.h
|
||||
RESOURCES += res/Synergy.qrc
|
||||
RC_FILE = res/win/Synergy.rc
|
||||
macx {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindowBase</class>
|
||||
<widget class="QMainWindow" name="MainWindowBase">
|
||||
|
@ -87,7 +87,7 @@
|
|||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>&Server (share this computer's mouse and keyboard):</string>
|
||||
<string>Ser&ver (share this computer's mouse and keyboard):</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
|
@ -117,6 +117,30 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="m_pLabelFingerprint">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Fingerprint:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="m_pLabelLocalFingerprint">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="m_pRadioInternalConfig">
|
||||
<property name="text">
|
||||
|
@ -128,7 +152,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QPushButton" name="m_pButtonConfigureServer">
|
||||
<property name="text">
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2015 Synergy Si 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 COPYING 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 "Fingerprint.h"
|
||||
|
||||
#include "CoreInterface.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QTextStream>
|
||||
|
||||
static const char kDirName[] = "SSL/Fingerprints";
|
||||
static const char kLocalFilename[] = "Local.txt";
|
||||
static const char kTrustedServersFilename[] = "TrustedServers.txt";
|
||||
static const char kTrustedClientsFilename[] = "TrustedClients.txt";
|
||||
|
||||
Fingerprint::Fingerprint(const QString& filename)
|
||||
{
|
||||
m_Filename = filename;
|
||||
}
|
||||
|
||||
void Fingerprint::trust(const QString& fingerprintText, bool append)
|
||||
{
|
||||
Fingerprint::persistDirectory();
|
||||
|
||||
QIODevice::OpenMode openMode;
|
||||
if (append) {
|
||||
openMode = QIODevice::Append;
|
||||
}
|
||||
else {
|
||||
openMode = QIODevice::WriteOnly;
|
||||
}
|
||||
|
||||
QFile file(filePath());
|
||||
if (file.open(openMode))
|
||||
{
|
||||
QTextStream out(&file);
|
||||
out << fingerprintText << "\n";
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
bool Fingerprint::fileExists() const
|
||||
{
|
||||
QString dirName = Fingerprint::directoryPath();
|
||||
if (!QDir(dirName).exists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile file(filePath());
|
||||
return file.exists();
|
||||
}
|
||||
|
||||
bool Fingerprint::isTrusted(const QString& fingerprintText)
|
||||
{
|
||||
QStringList list = readList();
|
||||
foreach (QString trusted, list)
|
||||
{
|
||||
if (trusted == fingerprintText) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList Fingerprint::readList(const int readTo)
|
||||
{
|
||||
QStringList list;
|
||||
|
||||
QString dirName = Fingerprint::directoryPath();
|
||||
if (!QDir(dirName).exists()) {
|
||||
return list;
|
||||
}
|
||||
|
||||
QFile file(filePath());
|
||||
|
||||
if (file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
QTextStream in(&file);
|
||||
while (!in.atEnd())
|
||||
{
|
||||
list.append(in.readLine());
|
||||
if (list.size() == readTo) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
QString Fingerprint::readFirst()
|
||||
{
|
||||
QStringList list = readList(1);
|
||||
return list.at(0);
|
||||
}
|
||||
|
||||
QString Fingerprint::filePath() const
|
||||
{
|
||||
QString dir = Fingerprint::directoryPath();
|
||||
return QString("%1/%2").arg(dir).arg(m_Filename);
|
||||
}
|
||||
|
||||
void Fingerprint::persistDirectory()
|
||||
{
|
||||
QDir dir(Fingerprint::directoryPath());
|
||||
if (!dir.exists()) {
|
||||
dir.mkpath(".");
|
||||
}
|
||||
}
|
||||
|
||||
QString Fingerprint::directoryPath()
|
||||
{
|
||||
CoreInterface coreInterface;
|
||||
QString profileDir = coreInterface.getProfileDir();
|
||||
|
||||
return QString("%1/%2")
|
||||
.arg(profileDir)
|
||||
.arg(kDirName);
|
||||
}
|
||||
|
||||
Fingerprint Fingerprint::local()
|
||||
{
|
||||
return Fingerprint(kLocalFilename);
|
||||
}
|
||||
|
||||
Fingerprint Fingerprint::trustedServers()
|
||||
{
|
||||
return Fingerprint(kTrustedServersFilename);
|
||||
}
|
||||
|
||||
Fingerprint Fingerprint::trustedClients()
|
||||
{
|
||||
return Fingerprint(kTrustedClientsFilename);
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2015 Synergy Si 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 COPYING 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
class Fingerprint
|
||||
{
|
||||
private:
|
||||
Fingerprint(const QString& filename);
|
||||
|
||||
public:
|
||||
void trust(const QString& fingerprintText, bool append = true);
|
||||
bool isTrusted(const QString& fingerprintText);
|
||||
QStringList readList(const int readTo = -1);
|
||||
QString readFirst();
|
||||
QString filePath() const;
|
||||
bool fileExists() const;
|
||||
|
||||
public:
|
||||
static Fingerprint local();
|
||||
static Fingerprint trustedServers();
|
||||
static Fingerprint trustedClients();
|
||||
static QString directoryPath();
|
||||
static QString localFingerprint();
|
||||
static bool localFingerprintExists();
|
||||
static void persistDirectory();
|
||||
|
||||
private:
|
||||
QString m_Filename;
|
||||
};
|
|
@ -21,6 +21,9 @@
|
|||
#include <iostream>
|
||||
|
||||
#include "MainWindow.h"
|
||||
|
||||
#include "Fingerprint.h"
|
||||
#include "PluginManager.h"
|
||||
#include "AboutDialog.h"
|
||||
#include "ServerConfigDialog.h"
|
||||
#include "SettingsDialog.h"
|
||||
|
@ -131,6 +134,8 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) :
|
|||
updateEdition();
|
||||
|
||||
m_pLabelPadlock->hide();
|
||||
|
||||
updateLocalFingerprint();
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
|
@ -395,6 +400,50 @@ void MainWindow::updateStateFromLogLine(const QString &line)
|
|||
{
|
||||
setSynergyState(synergyConnected);
|
||||
}
|
||||
|
||||
checkFingerprint(line);
|
||||
}
|
||||
|
||||
void MainWindow::checkFingerprint(const QString& line)
|
||||
{
|
||||
QRegExp fingerprintRegex(".*server fingerprint: ([A-F0-9:]+)");
|
||||
if (!fingerprintRegex.exactMatch(line)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString fingerprint = fingerprintRegex.cap(1);
|
||||
if (Fingerprint::trustedServers().isTrusted(fingerprint)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QMessageBox::StandardButton fingerprintReply =
|
||||
QMessageBox::information(
|
||||
this, tr("Security question"),
|
||||
tr("Do you trust this fingerprint?\n\n"
|
||||
"%1\n\n"
|
||||
"This is a server fingerprint. You should compare this "
|
||||
"fingerprint to the one on your server's screen. If the "
|
||||
"two don't match exactly, then it's probably not the server "
|
||||
"you're expecting (it could be a malicious user).\n\n"
|
||||
"To automatically trust this fingerprint for future "
|
||||
"connections, click Yes. To reject this fingerprint and "
|
||||
"disconnect from the server, click No.")
|
||||
.arg(fingerprint),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
|
||||
if (fingerprintReply == QMessageBox::Yes) {
|
||||
// restart core process after trusting fingerprint.
|
||||
Fingerprint::trustedServers().trust(fingerprint);
|
||||
startSynergy();
|
||||
}
|
||||
else {
|
||||
// on all platforms, the core process will stop if the
|
||||
// fingerprint is not trusted, so technically the stop
|
||||
// isn't really needed. however on windows, the core
|
||||
// process will keep trying (and failing) unless we
|
||||
// tell it to stop.
|
||||
stopSynergy();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::clearLog()
|
||||
|
@ -451,7 +500,11 @@ void MainWindow::startSynergy()
|
|||
}
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
args << "--profile-dir" << getProfileDirectoryForArg();
|
||||
// on windows, the profile directory changes depending on the user that
|
||||
// launched the process (e.g. when launched with elevation). setting the
|
||||
// profile dir on launch ensures it uses the same profile dir is used
|
||||
// no matter how its relaunched.
|
||||
args << "--profile-dir" << getProfileRootForArg();
|
||||
#endif
|
||||
|
||||
if ((synergyType() == synergyClient && !clientArgs(args, app))
|
||||
|
@ -492,14 +545,6 @@ void MainWindow::startSynergy()
|
|||
|
||||
if (desktopMode)
|
||||
{
|
||||
if (!appConfig().startedBefore()) {
|
||||
QMessageBox::information(
|
||||
this, "Synergy",
|
||||
tr("Synergy will be minimized to the notification "
|
||||
"area. This will happen automatically when Synergy "
|
||||
"starts."));
|
||||
}
|
||||
|
||||
synergyProcess()->start(app, args);
|
||||
if (!synergyProcess()->waitForStarted())
|
||||
{
|
||||
|
@ -757,12 +802,6 @@ void MainWindow::setSynergyState(qSynergyState state)
|
|||
setIcon(state);
|
||||
|
||||
m_SynergyState = state;
|
||||
|
||||
// if in desktop mode, hide synergy. in service mode the gui can
|
||||
// just be closed.
|
||||
if ((appConfig().processMode() == Desktop) && (state == synergyConnected)) {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::setVisible(bool visible)
|
||||
|
@ -893,6 +932,19 @@ void MainWindow::setEdition(int type)
|
|||
setWindowTitle(title);
|
||||
}
|
||||
|
||||
void MainWindow::updateLocalFingerprint()
|
||||
{
|
||||
if (Fingerprint::local().fileExists()) {
|
||||
m_pLabelFingerprint->setVisible(true);
|
||||
m_pLabelLocalFingerprint->setVisible(true);
|
||||
m_pLabelLocalFingerprint->setText(Fingerprint::local().readFirst());
|
||||
}
|
||||
else {
|
||||
m_pLabelFingerprint->setVisible(false);
|
||||
m_pLabelLocalFingerprint->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_m_pGroupClient_toggled(bool on)
|
||||
{
|
||||
m_pGroupServer->setChecked(!on);
|
||||
|
@ -1172,6 +1224,7 @@ void MainWindow::updateEdition()
|
|||
QString mac = getFirstMacAddress();
|
||||
QString hashSrc = m_AppConfig.activateEmail() + mac;
|
||||
QString hashResult = hash(hashSrc);
|
||||
|
||||
if (hashResult == m_AppConfig.userToken()) {
|
||||
setEdition(m_AppConfig.edition());
|
||||
}
|
||||
|
@ -1223,25 +1276,17 @@ void MainWindow::bonjourInstallFinished()
|
|||
m_pCheckBoxAutoConfig->setChecked(true);
|
||||
}
|
||||
|
||||
QString MainWindow::getProfileDirectory()
|
||||
QString MainWindow::getProfileRootForArg()
|
||||
{
|
||||
CoreInterface coreInterface;
|
||||
QString dir = coreInterface.getProfileDir();
|
||||
|
||||
// HACK: strip our app name since we're returning the root dir.
|
||||
#if defined(Q_OS_WIN)
|
||||
|
||||
QString qtDataDir = QDesktopServices::storageLocation(
|
||||
QDesktopServices::DataLocation);
|
||||
|
||||
// HACK: core wants the base app data dir, this seems like a very hacky
|
||||
// way to get it (maybe consider using %LOCALAPPDATA% instead?)
|
||||
return qtDataDir.replace("\\Synergy\\Synergy", "");
|
||||
|
||||
dir.replace("\\Synergy", "");
|
||||
#else
|
||||
|
||||
return "";
|
||||
|
||||
dir.replace("/.synergy", "");
|
||||
#endif
|
||||
}
|
||||
|
||||
QString MainWindow::getProfileDirectoryForArg()
|
||||
{
|
||||
return QString("\"%1\"").arg(getProfileDirectory());
|
||||
return QString("\"%1\"").arg(dir);
|
||||
}
|
||||
|
|
|
@ -106,6 +106,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase
|
|||
void updateZeroconfService();
|
||||
void serverDetected(const QString name);
|
||||
void setEdition(int type);
|
||||
void updateLocalFingerprint();
|
||||
|
||||
public slots:
|
||||
void appendLogRaw(const QString& text);
|
||||
|
@ -165,8 +166,8 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase
|
|||
void downloadBonjour();
|
||||
void promptAutoConfig();
|
||||
void updateEdition();
|
||||
QString getProfileDirectory();
|
||||
QString getProfileDirectoryForArg();
|
||||
QString getProfileRootForArg();
|
||||
void checkFingerprint(const QString& line);
|
||||
|
||||
private:
|
||||
QSettings& m_Settings;
|
||||
|
|
|
@ -22,10 +22,12 @@
|
|||
#include "DataDownloader.h"
|
||||
#include "QUtility.h"
|
||||
#include "ProcessorArch.h"
|
||||
#include "Fingerprint.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QProcess>
|
||||
#include <QCoreApplication>
|
||||
|
||||
static QString kBaseUrl = "http://synergy-project.org/files";
|
||||
static const char kWinProcessorArch32[] = "Windows-x86";
|
||||
|
@ -35,15 +37,9 @@ static const char kLinuxProcessorArchDeb32[] = "Linux-i686-deb";
|
|||
static const char kLinuxProcessorArchDeb64[] = "Linux-x86_64-deb";
|
||||
static const char kLinuxProcessorArchRpm32[] = "Linux-i686-rpm";
|
||||
static const char kLinuxProcessorArchRpm64[] = "Linux-x86_64-rpm";
|
||||
static QString kCertificateLifetime = "365";
|
||||
static QString kCertificateSubjectInfo = "/CN=Synergy";
|
||||
static QString kCertificateFilename = "Synergy.pem";
|
||||
static QString kUnixOpenSslCommand = "openssl";
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
static const char kWinPluginExt[] = ".dll";
|
||||
static const char kWinOpenSslSetup[] = "openssl-1.0.2-Windows-x86.exe";
|
||||
static const char kWinOpenSslBinary[] = "OpenSSL\\openssl.exe";
|
||||
|
||||
#elif defined(Q_OS_MAC)
|
||||
static const char kMacPluginPrefix[] = "lib";
|
||||
|
@ -72,10 +68,30 @@ PluginManager::~PluginManager()
|
|||
{
|
||||
}
|
||||
|
||||
bool PluginManager::exist(QString name)
|
||||
{
|
||||
CoreInterface coreInterface;
|
||||
QString PluginDir = coreInterface.getPluginDir();
|
||||
QString pluginName = getPluginOsSpecificName(name);
|
||||
QString filename;
|
||||
filename.append(PluginDir);
|
||||
filename.append(QDir::separator()).append(pluginName);
|
||||
QFile file(filename);
|
||||
bool exist = false;
|
||||
if (file.exists()) {
|
||||
exist = true;
|
||||
}
|
||||
|
||||
return exist;
|
||||
}
|
||||
|
||||
void PluginManager::downloadPlugins()
|
||||
{
|
||||
if (m_DataDownloader.isFinished()) {
|
||||
savePlugin();
|
||||
if (!savePlugin()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_DownloadIndex != m_PluginList.size() - 1) {
|
||||
emit downloadNext();
|
||||
}
|
||||
|
@ -101,59 +117,7 @@ void PluginManager::downloadPlugins()
|
|||
}
|
||||
}
|
||||
|
||||
void PluginManager::saveOpenSslSetup()
|
||||
{
|
||||
QDir dir(m_ProfileDir);
|
||||
if (!dir.exists()) {
|
||||
dir.mkpath(".");
|
||||
}
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
|
||||
QString filename =
|
||||
QString("%1\\%2")
|
||||
.arg(m_ProfileDir)
|
||||
.arg(kWinOpenSslSetup);
|
||||
|
||||
QFile file(filename);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
emit error(
|
||||
tr("Failed to save certificate tool to: %1")
|
||||
.arg(m_ProfileDir));
|
||||
return;
|
||||
}
|
||||
|
||||
file.write(m_DataDownloader.data());
|
||||
file.close();
|
||||
|
||||
QStringList installArgs;
|
||||
installArgs.append("-s");
|
||||
installArgs.append("-y");
|
||||
|
||||
if (!runProgram(filename, installArgs, QStringList())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// openssl installer no longer needed
|
||||
QFile::remove(filename);
|
||||
|
||||
#endif
|
||||
|
||||
emit openSslBinaryReady();
|
||||
}
|
||||
|
||||
void PluginManager::generateCertificate()
|
||||
{
|
||||
connect(
|
||||
this,
|
||||
SIGNAL(openSslBinaryReady()),
|
||||
this,
|
||||
SLOT(doGenerateCertificate()));
|
||||
|
||||
downloadOpenSslSetup();
|
||||
}
|
||||
|
||||
void PluginManager::savePlugin()
|
||||
bool PluginManager::savePlugin()
|
||||
{
|
||||
// create the path if not exist
|
||||
QDir dir(m_PluginDir);
|
||||
|
@ -169,15 +133,19 @@ void PluginManager::savePlugin()
|
|||
QFile file(filename);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
emit error(
|
||||
tr("Failed to download '%1' plugin to: %2")
|
||||
tr("Failed to download plugin '%1' to: %2\n%3")
|
||||
.arg(m_PluginList.at(m_DownloadIndex))
|
||||
.arg(m_PluginDir));
|
||||
.arg(m_PluginDir)
|
||||
.arg(file.errorString()));
|
||||
|
||||
return;
|
||||
file.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
file.write(m_DataDownloader.data());
|
||||
file.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QString PluginManager::getPluginUrl(const QString& pluginName)
|
||||
|
@ -236,19 +204,6 @@ QString PluginManager::getPluginUrl(const QString& pluginName)
|
|||
return result;
|
||||
}
|
||||
|
||||
QString PluginManager::getOpenSslSetupUrl()
|
||||
{
|
||||
QString result;
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
result = kBaseUrl;
|
||||
result.append("/tools/");
|
||||
result.append(kWinOpenSslSetup);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QString PluginManager::getPluginOsSpecificName(const QString& pluginName)
|
||||
{
|
||||
QString result = pluginName;
|
||||
|
@ -261,127 +216,3 @@ QString PluginManager::getPluginOsSpecificName(const QString& pluginName)
|
|||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
bool PluginManager::checkOpenSslBinary()
|
||||
{
|
||||
// assume OpenSsl is unavailable on Windows,
|
||||
// but always available on both Mac and Linux
|
||||
#if defined(Q_OS_WIN)
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void PluginManager::downloadOpenSslSetup()
|
||||
{
|
||||
if (checkOpenSslBinary()) {
|
||||
emit openSslBinaryReady();
|
||||
return;
|
||||
}
|
||||
|
||||
QString urlString = getOpenSslSetupUrl();
|
||||
|
||||
QUrl url;
|
||||
url.setUrl(urlString);
|
||||
|
||||
disconnect(
|
||||
&m_DataDownloader,
|
||||
SIGNAL(isComplete()),
|
||||
this,
|
||||
SLOT(downloadPlugins()));
|
||||
|
||||
connect(
|
||||
&m_DataDownloader,
|
||||
SIGNAL(isComplete()),
|
||||
this,
|
||||
SLOT(saveOpenSslSetup()));
|
||||
|
||||
m_DataDownloader.download(url);
|
||||
}
|
||||
|
||||
void PluginManager::doGenerateCertificate()
|
||||
{
|
||||
QString openSslProgramFile;
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
openSslProgramFile = m_ProfileDir;
|
||||
openSslProgramFile.append("\\").append(kWinOpenSslBinary);
|
||||
#else
|
||||
openSslProgramFile = kUnixOpenSslCommand;
|
||||
#endif
|
||||
|
||||
QStringList arguments;
|
||||
|
||||
// self signed certificate
|
||||
arguments.append("req");
|
||||
arguments.append("-x509");
|
||||
arguments.append("-nodes");
|
||||
|
||||
// valide duration
|
||||
arguments.append("-days");
|
||||
arguments.append(kCertificateLifetime);
|
||||
|
||||
// subject information
|
||||
arguments.append("-subj");
|
||||
|
||||
QString info(kCertificateSubjectInfo);
|
||||
arguments.append(info);
|
||||
|
||||
// private key
|
||||
arguments.append("-newkey");
|
||||
arguments.append("rsa:1024");
|
||||
|
||||
// key output filename
|
||||
arguments.append("-keyout");
|
||||
QString filename = m_ProfileDir;
|
||||
filename.append(QDir::separator()).append(kCertificateFilename);
|
||||
arguments.append(filename);
|
||||
|
||||
// certificate output filename
|
||||
arguments.append("-out");
|
||||
arguments.append(filename);
|
||||
|
||||
QStringList environment;
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
environment << QString("OPENSSL_CONF=%1\\OpenSSL\\synergy.conf")
|
||||
.arg(m_ProfileDir);
|
||||
#endif
|
||||
|
||||
if (!runProgram(openSslProgramFile, arguments, environment)) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit generateCertificateFinished();
|
||||
}
|
||||
|
||||
bool PluginManager::runProgram(
|
||||
const QString& program, const QStringList& args, const QStringList& env)
|
||||
{
|
||||
QProcess process;
|
||||
process.setEnvironment(env);
|
||||
process.start(program, args);
|
||||
|
||||
bool success = process.waitForStarted();
|
||||
|
||||
QString standardOutput, standardError;
|
||||
if (success && process.waitForFinished())
|
||||
{
|
||||
standardOutput = process.readAllStandardOutput().trimmed();
|
||||
standardError = process.readAllStandardError().trimmed();
|
||||
}
|
||||
|
||||
int code = process.exitCode();
|
||||
if (!success || code != 0)
|
||||
{
|
||||
emit error(
|
||||
QString("Program failed: %1\n\nCode: %2\nError: %3")
|
||||
.arg(program)
|
||||
.arg(process.exitCode())
|
||||
.arg(standardError.isEmpty() ? "Unknown" : standardError));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <QStringList>
|
||||
#include <QObject>
|
||||
|
||||
#include "SslCertificate.h"
|
||||
#include "CoreInterface.h"
|
||||
#include "DataDownloader.h"
|
||||
|
||||
|
@ -35,30 +36,26 @@ public:
|
|||
|
||||
int downloadIndex() { return m_DownloadIndex; }
|
||||
|
||||
static bool exist(QString name);
|
||||
|
||||
public slots:
|
||||
void downloadPlugins();
|
||||
void saveOpenSslSetup();
|
||||
void generateCertificate();
|
||||
void doGenerateCertificate();
|
||||
|
||||
private:
|
||||
void savePlugin();
|
||||
bool savePlugin();
|
||||
QString getPluginUrl(const QString& pluginName);
|
||||
QString getOpenSslSetupUrl();
|
||||
QString getPluginOsSpecificName(const QString& pluginName);
|
||||
bool checkOpenSslBinary();
|
||||
void downloadOpenSslSetup();
|
||||
bool runProgram(
|
||||
const QString& program,
|
||||
const QStringList& args,
|
||||
const QStringList& env);
|
||||
|
||||
static QString getPluginOsSpecificName(const QString& pluginName);
|
||||
|
||||
signals:
|
||||
void error(QString e);
|
||||
void info(QString i);
|
||||
void downloadNext();
|
||||
void downloadFinished();
|
||||
void openSslBinaryReady();
|
||||
void generateCertificateFinished();
|
||||
|
||||
private:
|
||||
QStringList m_PluginList;
|
||||
|
@ -67,6 +64,7 @@ private:
|
|||
int m_DownloadIndex;
|
||||
DataDownloader m_DataDownloader;
|
||||
CoreInterface m_CoreInterface;
|
||||
SslCertificate m_SslCertificate;
|
||||
};
|
||||
|
||||
#endif // PLUGINMANAGER_H
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "PluginWizardPage.h"
|
||||
#include "ui_PluginWizardPageBase.h"
|
||||
|
||||
#include "SslCertificate.h"
|
||||
#include "WebClient.h"
|
||||
#include "PluginManager.h"
|
||||
|
||||
|
@ -29,6 +30,7 @@ PluginWizardPage::PluginWizardPage(AppConfig& appConfig, QWidget *parent) :
|
|||
m_Finished(false),
|
||||
m_pWebClient(NULL),
|
||||
m_pPluginManager(NULL),
|
||||
m_pSslCertificate(NULL),
|
||||
m_AppConfig(appConfig)
|
||||
{
|
||||
setupUi(this);
|
||||
|
@ -36,6 +38,8 @@ PluginWizardPage::PluginWizardPage(AppConfig& appConfig, QWidget *parent) :
|
|||
QMovie *movie = new QMovie(":/res/image/spinning-wheel.gif");
|
||||
m_pLabelSpinning->setMovie(movie);
|
||||
movie->start();
|
||||
|
||||
m_pSslCertificate = new SslCertificate(this);
|
||||
}
|
||||
|
||||
PluginWizardPage::~PluginWizardPage()
|
||||
|
@ -47,6 +51,8 @@ PluginWizardPage::~PluginWizardPage()
|
|||
if (m_pPluginManager != NULL) {
|
||||
delete m_pPluginManager;
|
||||
}
|
||||
|
||||
delete m_pSslCertificate;
|
||||
}
|
||||
|
||||
void PluginWizardPage::changeEvent(QEvent *e)
|
||||
|
@ -101,20 +107,20 @@ void PluginWizardPage::finished()
|
|||
|
||||
void PluginWizardPage::generateCertificate()
|
||||
{
|
||||
connect(m_pPluginManager,
|
||||
SIGNAL(generateCertificateFinished()),
|
||||
connect(m_pSslCertificate,
|
||||
SIGNAL(generateFinished()),
|
||||
this,
|
||||
SLOT(finished()));
|
||||
|
||||
connect(m_pPluginManager,
|
||||
SIGNAL(generateCertificateFinished()),
|
||||
m_pPluginManagerThread,
|
||||
connect(m_pSslCertificate,
|
||||
SIGNAL(generateFinished()),
|
||||
m_pThread,
|
||||
SLOT(quit()));
|
||||
|
||||
updateStatus(tr("Generating SSL certificate..."));
|
||||
|
||||
QMetaObject::invokeMethod(
|
||||
m_pPluginManager,
|
||||
m_pSslCertificate,
|
||||
"generateCertificate",
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
@ -128,13 +134,18 @@ void PluginWizardPage::downloadPlugins()
|
|||
{
|
||||
QStringList pluginList = m_pWebClient->getPluginList();
|
||||
m_pPluginManager = new PluginManager(pluginList);
|
||||
m_pPluginManagerThread = new QThread;
|
||||
m_pThread = new QThread;
|
||||
|
||||
connect(m_pPluginManager,
|
||||
SIGNAL(error(QString)),
|
||||
this,
|
||||
SLOT(showError(QString)));
|
||||
|
||||
connect(m_pPluginManager,
|
||||
SIGNAL(info(QString)),
|
||||
this,
|
||||
SLOT(updateStatus(QString)));
|
||||
|
||||
connect(m_pPluginManager,
|
||||
SIGNAL(downloadNext()),
|
||||
this,
|
||||
|
@ -147,12 +158,12 @@ void PluginWizardPage::downloadPlugins()
|
|||
|
||||
connect(m_pPluginManager,
|
||||
SIGNAL(error(QString)),
|
||||
m_pPluginManagerThread,
|
||||
m_pThread,
|
||||
SLOT(quit()));
|
||||
|
||||
connect(m_pPluginManagerThread,
|
||||
connect(m_pThread,
|
||||
SIGNAL(finished()),
|
||||
m_pPluginManagerThread,
|
||||
m_pThread,
|
||||
SLOT(deleteLater()));
|
||||
|
||||
updateStatus(
|
||||
|
@ -160,8 +171,8 @@ void PluginWizardPage::downloadPlugins()
|
|||
.arg(pluginList.at(0))
|
||||
.arg(pluginList.size()));
|
||||
|
||||
m_pPluginManager->moveToThread(m_pPluginManagerThread);
|
||||
m_pPluginManagerThread->start();
|
||||
m_pPluginManager->moveToThread(m_pThread);
|
||||
m_pThread->start();
|
||||
|
||||
QMetaObject::invokeMethod(
|
||||
m_pPluginManager,
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
class WebClient;
|
||||
class PluginManager;
|
||||
class SslCertificate;
|
||||
|
||||
class PluginWizardPage : public QWizardPage, public Ui::PluginWizardPage {
|
||||
|
||||
|
@ -46,13 +47,13 @@ protected:
|
|||
|
||||
protected slots:
|
||||
void showError(QString error);
|
||||
void updateStatus(QString info);
|
||||
void queryPluginDone();
|
||||
void updateDownloadStatus();
|
||||
void finished();
|
||||
void generateCertificate();
|
||||
|
||||
private:
|
||||
void updateStatus(QString info);
|
||||
void downloadPlugins();
|
||||
void showFinished();
|
||||
|
||||
|
@ -62,7 +63,8 @@ private:
|
|||
QString m_Password;
|
||||
WebClient* m_pWebClient;
|
||||
PluginManager* m_pPluginManager;
|
||||
QThread* m_pPluginManagerThread;
|
||||
SslCertificate* m_pSslCertificate;
|
||||
QThread* m_pThread;
|
||||
AppConfig& m_AppConfig;
|
||||
};
|
||||
#endif // PLUGINWIZARDPAGE_H
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "SettingsDialog.h"
|
||||
|
||||
#include "PluginManager.h"
|
||||
#include "CoreInterface.h"
|
||||
#include "SynergyLocale.h"
|
||||
#include "QSynergyApplication.h"
|
||||
|
@ -30,6 +31,8 @@
|
|||
#include <QFileDialog>
|
||||
#include <QDir>
|
||||
|
||||
static const char networkSecurity[] = "ns";
|
||||
|
||||
SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) :
|
||||
QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
|
||||
Ui::SettingsDialogBase(),
|
||||
|
@ -57,10 +60,7 @@ SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) :
|
|||
m_pCheckBoxElevateMode->hide();
|
||||
#endif
|
||||
|
||||
QString pluginDir = m_CoreInterface.getPluginDir();
|
||||
QDir dir(pluginDir);
|
||||
int fileNum = dir.entryInfoList(QDir::NoDotAndDotDot|QDir::AllEntries).count();
|
||||
if (fileNum == 0) {
|
||||
if (!PluginManager::exist(networkSecurity)) {
|
||||
m_pGroupNetworkSecurity->setEnabled(false);
|
||||
m_pCheckBoxEnableCrypto->setChecked(false);
|
||||
}
|
||||
|
|
|
@ -169,6 +169,7 @@ void SetupWizard::accept()
|
|||
appConfig.setEdition(m_Edition);
|
||||
}
|
||||
m_MainWindow.setEdition(m_Edition);
|
||||
m_MainWindow.updateLocalFingerprint();
|
||||
|
||||
settings.sync();
|
||||
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2015 Synergy Si 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 COPYING 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 "SslCertificate.h"
|
||||
|
||||
#include "Fingerprint.h"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QDir>
|
||||
#include <QCoreApplication>
|
||||
|
||||
static const char kCertificateLifetime[] = "365";
|
||||
static const char kCertificateSubjectInfo[] = "/CN=Synergy";
|
||||
static const char kCertificateFilename[] = "Synergy.pem";
|
||||
static const char kSslDir[] = "SSL";
|
||||
static const char kUnixOpenSslCommand[] = "openssl";
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
static const char kWinOpenSslBinary[] = "OpenSSL\\openssl.exe";
|
||||
static const char kConfigFile[] = "OpenSSL\\synergy.conf";
|
||||
#endif
|
||||
|
||||
SslCertificate::SslCertificate(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
m_ProfileDir = m_CoreInterface.getProfileDir();
|
||||
if (m_ProfileDir.isEmpty()) {
|
||||
emit error(tr("Failed to get profile directory."));
|
||||
}
|
||||
}
|
||||
|
||||
bool SslCertificate::runTool(const QStringList& args)
|
||||
{
|
||||
QString program;
|
||||
#if defined(Q_OS_WIN)
|
||||
program = QCoreApplication::applicationDirPath();
|
||||
program.append("\\").append(kWinOpenSslBinary);
|
||||
#else
|
||||
program = kUnixOpenSslCommand;
|
||||
#endif
|
||||
|
||||
|
||||
QStringList environment;
|
||||
#if defined(Q_OS_WIN)
|
||||
environment << QString("OPENSSL_CONF=%1\\%2")
|
||||
.arg(QCoreApplication::applicationDirPath())
|
||||
.arg(kConfigFile);
|
||||
#endif
|
||||
|
||||
QProcess process;
|
||||
process.setEnvironment(environment);
|
||||
process.start(program, args);
|
||||
|
||||
bool success = process.waitForStarted();
|
||||
|
||||
QString standardError;
|
||||
if (success && process.waitForFinished())
|
||||
{
|
||||
m_ToolOutput = process.readAllStandardOutput().trimmed();
|
||||
standardError = process.readAllStandardError().trimmed();
|
||||
}
|
||||
|
||||
int code = process.exitCode();
|
||||
if (!success || code != 0)
|
||||
{
|
||||
emit error(
|
||||
QString("SSL tool failed: %1\n\nCode: %2\nError: %3")
|
||||
.arg(program)
|
||||
.arg(process.exitCode())
|
||||
.arg(standardError.isEmpty() ? "Unknown" : standardError));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SslCertificate::generateCertificate()
|
||||
{
|
||||
QStringList arguments;
|
||||
|
||||
// self signed certificate
|
||||
arguments.append("req");
|
||||
arguments.append("-x509");
|
||||
arguments.append("-nodes");
|
||||
|
||||
// valide duration
|
||||
arguments.append("-days");
|
||||
arguments.append(kCertificateLifetime);
|
||||
|
||||
// subject information
|
||||
arguments.append("-subj");
|
||||
|
||||
QString subInfo(kCertificateSubjectInfo);
|
||||
arguments.append(subInfo);
|
||||
|
||||
// private key
|
||||
arguments.append("-newkey");
|
||||
arguments.append("rsa:1024");
|
||||
|
||||
QString sslDirPath = QString("%1%2%3")
|
||||
.arg(m_ProfileDir)
|
||||
.arg(QDir::separator())
|
||||
.arg(kSslDir);
|
||||
|
||||
QDir sslDir(sslDirPath);
|
||||
if (!sslDir.exists()) {
|
||||
sslDir.mkpath(".");
|
||||
}
|
||||
|
||||
QString filename = QString("%1%2%3")
|
||||
.arg(sslDirPath)
|
||||
.arg(QDir::separator())
|
||||
.arg(kCertificateFilename);
|
||||
|
||||
// key output filename
|
||||
arguments.append("-keyout");
|
||||
arguments.append(filename);
|
||||
|
||||
// certificate output filename
|
||||
arguments.append("-out");
|
||||
arguments.append(filename);
|
||||
|
||||
if (!runTool(arguments)) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit info(tr("SSL certificate generated."));
|
||||
|
||||
generateFingerprint(filename);
|
||||
|
||||
emit generateFinished();
|
||||
}
|
||||
|
||||
void SslCertificate::generateFingerprint(const QString& certificateFilename)
|
||||
{
|
||||
QStringList arguments;
|
||||
arguments.append("x509");
|
||||
arguments.append("-fingerprint");
|
||||
arguments.append("-sha1");
|
||||
arguments.append("-noout");
|
||||
arguments.append("-in");
|
||||
arguments.append(certificateFilename);
|
||||
|
||||
if (!runTool(arguments)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// find the fingerprint from the tool output
|
||||
int i = m_ToolOutput.indexOf("=");
|
||||
if (i != -1) {
|
||||
i++;
|
||||
QString fingerprint = m_ToolOutput.mid(
|
||||
i, m_ToolOutput.size() - i);
|
||||
|
||||
Fingerprint::local().trust(fingerprint, false);
|
||||
emit info(tr("SSL fingerprint generated."));
|
||||
}
|
||||
else {
|
||||
emit error(tr("Failed to find SSL fingerprint."));
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012 Synergy Si Ltd.
|
||||
* Copyright (C) 2011 Nick Bolton
|
||||
* Copyright (C) 2015 Synergy Si Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -18,20 +17,31 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#define TEST_ENV
|
||||
#include "CoreInterface.h"
|
||||
|
||||
#include "client/Client.h"
|
||||
#include <QObject>
|
||||
|
||||
#include "test/global/gmock.h"
|
||||
|
||||
class IEventQueue;
|
||||
|
||||
class MockClient : public Client
|
||||
class SslCertificate : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MockClient() : Client() { }
|
||||
MOCK_METHOD2(mouseMove, void(SInt32, SInt32));
|
||||
MOCK_METHOD1(setOptions, void(const OptionsList&));
|
||||
MOCK_METHOD0(handshakeComplete, void());
|
||||
MOCK_METHOD1(setDecryptIv, void(const UInt8*));
|
||||
explicit SslCertificate(QObject *parent = 0);
|
||||
|
||||
public slots:
|
||||
void generateCertificate();
|
||||
|
||||
signals:
|
||||
void error(QString e);
|
||||
void info(QString i);
|
||||
void generateFinished();
|
||||
|
||||
private:
|
||||
bool runTool(const QStringList& args);
|
||||
void generateFingerprint(const QString& certificateFilename);
|
||||
|
||||
private:
|
||||
QString m_ProfileDir;
|
||||
QString m_ToolOutput;
|
||||
CoreInterface m_CoreInterface;
|
||||
};
|
|
@ -95,6 +95,7 @@ REGISTER_EVENT(IListenSocket, connecting)
|
|||
//
|
||||
|
||||
REGISTER_EVENT(ISocket, disconnected)
|
||||
REGISTER_EVENT(ISocket, stopRetry)
|
||||
|
||||
//
|
||||
// OSXScreen
|
||||
|
|
|
@ -281,7 +281,8 @@ private:
|
|||
class ISocketEvents : public EventTypes {
|
||||
public:
|
||||
ISocketEvents() :
|
||||
m_disconnected(Event::kUnknown) { }
|
||||
m_disconnected(Event::kUnknown),
|
||||
m_stopRetry(Event::kUnknown) { }
|
||||
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
@ -294,10 +295,18 @@ public:
|
|||
*/
|
||||
Event::Type disconnected();
|
||||
|
||||
//! Get stop retry event type
|
||||
/*!
|
||||
Returns the stop retry event type. This is sent when the client
|
||||
doesn't want to reconnect after it disconnects from the server.
|
||||
*/
|
||||
Event::Type stopRetry();
|
||||
|
||||
//@}
|
||||
|
||||
private:
|
||||
Event::Type m_disconnected;
|
||||
Event::Type m_stopRetry;
|
||||
};
|
||||
|
||||
class OSXScreenEvents : public EventTypes {
|
||||
|
|
|
@ -27,6 +27,9 @@
|
|||
#include <algorithm>
|
||||
#include <stdio.h>
|
||||
#include <cstdarg>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
|
||||
namespace synergy {
|
||||
namespace string {
|
||||
|
@ -180,6 +183,30 @@ removeFileExt(String filename)
|
|||
return filename.substr(0, dot);
|
||||
}
|
||||
|
||||
void
|
||||
toHex(String& subject, int width, const char fill)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::hex;
|
||||
for (unsigned int i = 0; i < subject.length(); i++) {
|
||||
ss << std::setw(width) << std::setfill(fill) << (int)(unsigned char)subject[i];
|
||||
}
|
||||
|
||||
subject = ss.str();
|
||||
}
|
||||
|
||||
void
|
||||
uppercase(String& subject)
|
||||
{
|
||||
std::transform(subject.begin(), subject.end(), subject.begin(), ::toupper);
|
||||
}
|
||||
|
||||
void
|
||||
removeChar(String& subject, const char c)
|
||||
{
|
||||
subject.erase(std::remove(subject.begin(), subject.end(), c), subject.end());
|
||||
}
|
||||
|
||||
//
|
||||
// CaselessCmp
|
||||
//
|
||||
|
|
|
@ -70,6 +70,25 @@ Finds the last dot and remove all characters from the dot to the end
|
|||
*/
|
||||
String removeFileExt(String filename);
|
||||
|
||||
//! Convert into hexdecimal
|
||||
/*!
|
||||
Convert each character in \c subject into hexdecimal form with \c width
|
||||
*/
|
||||
void toHex(String& subject, int width, const char fill = '0');
|
||||
|
||||
//! Convert to all uppercase
|
||||
/*!
|
||||
Convert each character in \c subject to uppercase
|
||||
*/
|
||||
void uppercase(String& subject);
|
||||
|
||||
//! Remove all specific char in suject
|
||||
/*!
|
||||
Remove all specific \c char in \c suject
|
||||
*/
|
||||
void removeChar(String& subject, const char c);
|
||||
|
||||
|
||||
//! Case-insensitive comparisons
|
||||
/*!
|
||||
This class provides case-insensitve comparison functions.
|
||||
|
|
|
@ -60,8 +60,7 @@ Client::Client(
|
|||
const String& name, const NetworkAddress& address,
|
||||
ISocketFactory* socketFactory,
|
||||
synergy::Screen* screen,
|
||||
bool enableDragDrop,
|
||||
bool enableCrypto) :
|
||||
ClientArgs& args) :
|
||||
m_mock(false),
|
||||
m_name(name),
|
||||
m_serverAddress(address),
|
||||
|
@ -77,9 +76,9 @@ Client::Client(
|
|||
m_events(events),
|
||||
m_sendFileThread(NULL),
|
||||
m_writeToDropDirThread(NULL),
|
||||
m_enableDragDrop(enableDragDrop),
|
||||
m_socket(NULL),
|
||||
m_useSecureNetwork(false)
|
||||
m_useSecureNetwork(false),
|
||||
m_args(args)
|
||||
{
|
||||
assert(m_socketFactory != NULL);
|
||||
assert(m_screen != NULL);
|
||||
|
@ -94,7 +93,7 @@ Client::Client(
|
|||
new TMethodEventJob<Client>(this,
|
||||
&Client::handleResume));
|
||||
|
||||
if (m_enableDragDrop) {
|
||||
if (m_args.m_enableDragDrop) {
|
||||
m_events->adoptHandler(m_events->forIScreen().fileChunkSending(),
|
||||
this,
|
||||
new TMethodEventJob<Client>(this,
|
||||
|
@ -105,7 +104,7 @@ Client::Client(
|
|||
&Client::handleFileRecieveCompleted));
|
||||
}
|
||||
|
||||
if (enableCrypto) {
|
||||
if (m_args.m_enableCrypto) {
|
||||
m_useSecureNetwork = ARCH->plugin().exists(s_networkSecurity);
|
||||
if (m_useSecureNetwork == false) {
|
||||
LOG((CLOG_NOTE "crypto disabled because of ns plugin not available"));
|
||||
|
@ -471,6 +470,10 @@ Client::setupConnection()
|
|||
m_stream->getEventTarget(),
|
||||
new TMethodEventJob<Client>(this,
|
||||
&Client::handleDisconnected));
|
||||
|
||||
m_events->adoptHandler(m_events->forISocket().stopRetry(),
|
||||
m_stream->getEventTarget(),
|
||||
new TMethodEventJob<Client>(this, &Client::handleStopRetry));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -526,6 +529,8 @@ Client::cleanupConnection()
|
|||
m_stream->getEventTarget());
|
||||
m_events->removeHandler(m_events->forISocket().disconnected(),
|
||||
m_stream->getEventTarget());
|
||||
m_events->removeHandler(m_events->forISocket().stopRetry(),
|
||||
m_stream->getEventTarget());
|
||||
cleanupStream();
|
||||
}
|
||||
}
|
||||
|
@ -744,6 +749,11 @@ Client::onFileRecieveCompleted()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Client::handleStopRetry(const Event&, void*)
|
||||
{
|
||||
m_args.m_restartable = false;
|
||||
}
|
||||
|
||||
void
|
||||
Client::writeToDropDirThread(void*)
|
||||
|
@ -780,7 +790,8 @@ Client::fileChunkReceived(String data)
|
|||
void
|
||||
Client::dragInfoReceived(UInt32 fileNum, String data)
|
||||
{
|
||||
if (!m_enableDragDrop) {
|
||||
// TODO: fix duplicate function from CServer
|
||||
if (!m_args.m_enableDragDrop) {
|
||||
LOG((CLOG_DEBUG "drag drop not enabled, ignoring drag info."));
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "synergy/IClipboard.h"
|
||||
#include "synergy/DragInformation.h"
|
||||
#include "synergy/INode.h"
|
||||
#include "synergy/ClientArgs.h"
|
||||
#include "net/NetworkAddress.h"
|
||||
#include "base/EventTypes.h"
|
||||
|
||||
|
@ -59,14 +60,9 @@ public:
|
|||
const String& name, const NetworkAddress& address,
|
||||
ISocketFactory* socketFactory,
|
||||
synergy::Screen* screen,
|
||||
bool enableDragDrop,
|
||||
bool enableCrypto);
|
||||
ClientArgs& args);
|
||||
~Client();
|
||||
|
||||
#ifdef TEST_ENV
|
||||
Client() : m_mock(true) { }
|
||||
#endif
|
||||
|
||||
//! @name manipulators
|
||||
//@{
|
||||
|
||||
|
@ -196,6 +192,7 @@ private:
|
|||
void handleResume(const Event& event, void*);
|
||||
void handleFileChunkSending(const Event&, void*);
|
||||
void handleFileRecieveCompleted(const Event&, void*);
|
||||
void handleStopRetry(const Event&, void*);
|
||||
void onFileRecieveCompleted();
|
||||
|
||||
public:
|
||||
|
@ -224,7 +221,7 @@ private:
|
|||
String m_dragFileExt;
|
||||
Thread* m_sendFileThread;
|
||||
Thread* m_writeToDropDirThread;
|
||||
bool m_enableDragDrop;
|
||||
TCPSocket* m_socket;
|
||||
bool m_useSecureNetwork;
|
||||
ClientArgs& m_args;
|
||||
};
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
#include "mt/Thread.h"
|
||||
|
||||
#include "net/XSocket.h"
|
||||
#include "mt/XMT.h"
|
||||
#include "mt/XThread.h"
|
||||
#include "arch/Arch.h"
|
||||
|
@ -158,11 +157,6 @@ Thread::threadFunc(void* vjob)
|
|||
job->run();
|
||||
LOG((CLOG_DEBUG1 "thread 0x%08x exit", id));
|
||||
}
|
||||
|
||||
catch (XSocket& e) {
|
||||
// client called cancel()
|
||||
LOG((CLOG_DEBUG "%s", e.what()));
|
||||
}
|
||||
catch (XThreadCancel&) {
|
||||
// client called cancel()
|
||||
LOG((CLOG_DEBUG1 "caught cancel on thread 0x%08x", id));
|
||||
|
|
|
@ -112,27 +112,35 @@ TCPListenSocket::accept()
|
|||
try {
|
||||
socket = new TCPSocket(m_events, m_socketMultiplexer, ARCH->acceptSocket(m_socket, NULL));
|
||||
if (socket != NULL) {
|
||||
m_socketMultiplexer->addSocket(this,
|
||||
new TSocketMultiplexerMethodJob<TCPListenSocket>(
|
||||
this, &TCPListenSocket::serviceListening,
|
||||
m_socket, true, false));
|
||||
setListeningJob();
|
||||
}
|
||||
return socket;
|
||||
}
|
||||
catch (XArchNetwork&) {
|
||||
if (socket != NULL) {
|
||||
delete socket;
|
||||
setListeningJob();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
if (socket != NULL) {
|
||||
delete socket;
|
||||
setListeningJob();
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TCPListenSocket::setListeningJob()
|
||||
{
|
||||
m_socketMultiplexer->addSocket(this,
|
||||
new TSocketMultiplexerMethodJob<TCPListenSocket>(
|
||||
this, &TCPListenSocket::serviceListening,
|
||||
m_socket, true, false));
|
||||
}
|
||||
|
||||
ISocketMultiplexerJob*
|
||||
TCPListenSocket::serviceListening(ISocketMultiplexerJob* job,
|
||||
bool read, bool, bool error)
|
||||
|
|
|
@ -45,6 +45,9 @@ public:
|
|||
accept();
|
||||
virtual void deleteSocket(void*) { }
|
||||
|
||||
protected:
|
||||
void setListeningJob();
|
||||
|
||||
public:
|
||||
ISocketMultiplexerJob*
|
||||
serviceListening(ISocketMultiplexerJob*,
|
||||
|
|
|
@ -461,15 +461,26 @@ TCPSocket::serviceConnected(ISocketMultiplexerJob* job,
|
|||
}
|
||||
|
||||
bool needNewJob = false;
|
||||
static UInt32 s_retryOutputBufferSize = 0;
|
||||
|
||||
if (write) {
|
||||
try {
|
||||
// write data
|
||||
UInt32 n = m_outputBuffer.getSize();
|
||||
|
||||
if (s_retryOutputBufferSize > 0) {
|
||||
n = s_retryOutputBufferSize;
|
||||
}
|
||||
|
||||
const void* buffer = m_outputBuffer.peek(n);
|
||||
if (isSecure()) {
|
||||
if (isSecureReady()) {
|
||||
s_retryOutputBufferSize = n;
|
||||
n = secureWrite(buffer, n);
|
||||
|
||||
if (n > 0) {
|
||||
s_retryOutputBufferSize = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return job;
|
||||
|
@ -519,7 +530,8 @@ TCPSocket::serviceConnected(ISocketMultiplexerJob* job,
|
|||
|
||||
if (read && m_readable) {
|
||||
try {
|
||||
UInt8 buffer[4096];
|
||||
static UInt8 buffer[4096];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
size_t n = 0;
|
||||
|
||||
if (isSecure()) {
|
||||
|
|
|
@ -59,6 +59,7 @@ public:
|
|||
|
||||
virtual void secureConnect() {}
|
||||
virtual void secureAccept() {}
|
||||
virtual void setFingerprintFilename(String& f) {}
|
||||
|
||||
protected:
|
||||
ArchSocket getSocket() { return m_socket; }
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "net/TSocketMultiplexerMethodJob.h"
|
||||
#include "arch/XArch.h"
|
||||
|
||||
static const char s_certificateDir[] = { "SSL" };
|
||||
static const char s_certificateFilename[] = { "Synergy.pem" };
|
||||
|
||||
//
|
||||
|
@ -54,39 +55,41 @@ SecureListenSocket::accept()
|
|||
m_events,
|
||||
m_socketMultiplexer,
|
||||
ARCH->acceptSocket(m_socket, NULL));
|
||||
socket->initSsl(true);
|
||||
|
||||
if (socket != NULL) {
|
||||
setListeningJob();
|
||||
}
|
||||
|
||||
String certificateFilename = synergy::string::sprintf(
|
||||
"%s/%s/%s",
|
||||
ARCH->getProfileDirectory().c_str(),
|
||||
s_certificateDir,
|
||||
s_certificateFilename);
|
||||
|
||||
bool loaded = socket->loadCertificates(certificateFilename);
|
||||
if (!loaded) {
|
||||
delete socket;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
socket->secureAccept();
|
||||
|
||||
m_secureSocketSet.insert(socket);
|
||||
|
||||
socket->initSsl(true);
|
||||
// TODO: customized certificate path
|
||||
String certificateFilename = ARCH->getProfileDirectory();
|
||||
#if SYSAPI_WIN32
|
||||
certificateFilename.append("\\");
|
||||
#elif SYSAPI_UNIX
|
||||
certificateFilename.append("/");
|
||||
#endif
|
||||
certificateFilename.append(s_certificateFilename);
|
||||
|
||||
socket->loadCertificates(certificateFilename.c_str());
|
||||
socket->secureAccept();
|
||||
|
||||
if (socket != NULL) {
|
||||
m_socketMultiplexer->addSocket(this,
|
||||
new TSocketMultiplexerMethodJob<TCPListenSocket>(
|
||||
this, &TCPListenSocket::serviceListening,
|
||||
m_socket, true, false));
|
||||
}
|
||||
return dynamic_cast<IDataSocket*>(socket);
|
||||
}
|
||||
catch (XArchNetwork&) {
|
||||
if (socket != NULL) {
|
||||
delete socket;
|
||||
setListeningJob();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
if (socket != NULL) {
|
||||
delete socket;
|
||||
setListeningJob();
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
|
||||
//
|
||||
// SecureSocket
|
||||
|
@ -35,6 +36,11 @@
|
|||
|
||||
#define MAX_ERROR_SIZE 65535
|
||||
|
||||
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;
|
||||
|
@ -149,24 +155,46 @@ SecureSocket::initSsl(bool server)
|
|||
initContext(server);
|
||||
}
|
||||
|
||||
void
|
||||
SecureSocket::loadCertificates(const char* filename)
|
||||
bool
|
||||
SecureSocket::loadCertificates(String& filename)
|
||||
{
|
||||
int r = 0;
|
||||
r = SSL_CTX_use_certificate_file(m_ssl->m_context, filename, SSL_FILETYPE_PEM);
|
||||
if (r <= 0) {
|
||||
throwError("could not use ssl certificate");
|
||||
if (filename.empty()) {
|
||||
showError("ssl certificate is not specified");
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
std::ifstream file(filename.c_str());
|
||||
bool exist = file.good();
|
||||
file.close();
|
||||
|
||||
if (!exist) {
|
||||
String errorMsg("ssl certificate doesn't exist: ");
|
||||
errorMsg.append(filename);
|
||||
showError(errorMsg.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
r = SSL_CTX_use_PrivateKey_file(m_ssl->m_context, filename, SSL_FILETYPE_PEM);
|
||||
int r = 0;
|
||||
r = SSL_CTX_use_certificate_file(m_ssl->m_context, filename.c_str(), SSL_FILETYPE_PEM);
|
||||
if (r <= 0) {
|
||||
throwError("could not use ssl private key");
|
||||
showError("could not use ssl 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 ssl private key");
|
||||
return false;
|
||||
}
|
||||
|
||||
r = SSL_CTX_check_private_key(m_ssl->m_context);
|
||||
if (!r) {
|
||||
throwError("could not verify ssl private key");
|
||||
showError("could not verify ssl private key");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -253,23 +281,30 @@ SecureSocket::secureConnect(int socket)
|
|||
checkResult(r, fatal, retry);
|
||||
|
||||
if (fatal) {
|
||||
// tell user and sleep so the socket isn't hammered.
|
||||
LOG((CLOG_ERR "failed to connect secure socket"));
|
||||
LOG((CLOG_INFO "server connection may not be secure"));
|
||||
ARCH->sleep(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_secureReady = !retry;
|
||||
|
||||
if (m_secureReady) {
|
||||
LOG((CLOG_INFO "connected to secure socket"));
|
||||
showCertificate();
|
||||
if (verifyCertFingerprint()) {
|
||||
LOG((CLOG_INFO "connected to secure socket"));
|
||||
if (!showCertificate()) {
|
||||
disconnect();
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG((CLOG_ERR "failed to verify server certificate fingerprint"));
|
||||
disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
return retry;
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
SecureSocket::showCertificate()
|
||||
{
|
||||
X509* cert;
|
||||
|
@ -284,8 +319,11 @@ SecureSocket::showCertificate()
|
|||
X509_free(cert);
|
||||
}
|
||||
else {
|
||||
throwError("server has no ssl certificate");
|
||||
showError("server has no ssl certificate");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -330,6 +368,21 @@ SecureSocket::checkResult(int n, bool& fatal, bool& retry)
|
|||
|
||||
case SSL_ERROR_SYSCALL:
|
||||
LOG((CLOG_ERR "secure socket error: SSL_ERROR_SYSCALL"));
|
||||
if (ERR_peek_error() == 0) {
|
||||
if (n == 0) {
|
||||
LOG((CLOG_ERR "an EOF violates the protocol"));
|
||||
}
|
||||
else if (n == -1) {
|
||||
// underlying socket I/O reproted an error
|
||||
try {
|
||||
ARCH->throwErrorOnSocket(getSocket());
|
||||
}
|
||||
catch (XArchNetwork& e) {
|
||||
LOG((CLOG_ERR "%s", e.what()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fatal = true;
|
||||
break;
|
||||
|
||||
|
@ -339,37 +392,27 @@ SecureSocket::checkResult(int n, bool& fatal, bool& retry)
|
|||
break;
|
||||
|
||||
default:
|
||||
LOG((CLOG_ERR "secure socket error: SSL_ERROR_SSL"));
|
||||
LOG((CLOG_ERR "secure socket error: unknown"));
|
||||
fatal = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fatal) {
|
||||
showError();
|
||||
sendEvent(getEvents()->forISocket().disconnected());
|
||||
sendEvent(getEvents()->forIStream().inputShutdown());
|
||||
disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SecureSocket::showError()
|
||||
SecureSocket::showError(const char* reason)
|
||||
{
|
||||
String error = getError();
|
||||
if (!error.empty()) {
|
||||
LOG((CLOG_ERR "secure socket error: %s", error.c_str()));
|
||||
if (reason != NULL) {
|
||||
LOG((CLOG_ERR "%s", reason));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SecureSocket::throwError(const char* reason)
|
||||
{
|
||||
String error = getError();
|
||||
if (!error.empty()) {
|
||||
throw XSocket(synergy::string::sprintf(
|
||||
"%s: %s", reason, error.c_str()));
|
||||
}
|
||||
else {
|
||||
throw XSocket(reason);
|
||||
LOG((CLOG_ERR "%s", error.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -388,6 +431,82 @@ SecureSocket::getError()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
SecureSocket::disconnect()
|
||||
{
|
||||
sendEvent(getEvents()->forISocket().stopRetry());
|
||||
sendEvent(getEvents()->forISocket().disconnected());
|
||||
sendEvent(getEvents()->forIStream().inputShutdown());
|
||||
}
|
||||
|
||||
void
|
||||
SecureSocket::formatFingerprint(String& fingerprint, bool hex, bool separator)
|
||||
{
|
||||
if (hex) {
|
||||
// to hexidecimal
|
||||
synergy::string::toHex(fingerprint, 2);
|
||||
}
|
||||
|
||||
// all uppercase
|
||||
synergy::string::uppercase(fingerprint);
|
||||
|
||||
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));
|
||||
return false;
|
||||
}
|
||||
|
||||
// format fingerprint into hexdecimal format with colon separator
|
||||
String fingerprint(reinterpret_cast<char*>(tempFingerprint), tempFingerprintLen);
|
||||
formatFingerprint(fingerprint);
|
||||
LOG((CLOG_NOTE "server fingerprint: %s", fingerprint.c_str()));
|
||||
|
||||
String trustedServersFilename;
|
||||
trustedServersFilename = synergy::string::sprintf(
|
||||
"%s/%s/%s",
|
||||
ARCH->getProfileDirectory().c_str(),
|
||||
kFingerprintDirName,
|
||||
kFingerprintTrustedServersFilename);
|
||||
|
||||
// check if this fingerprint exist
|
||||
String fileLine;
|
||||
std::ifstream file;
|
||||
file.open(trustedServersFilename.c_str());
|
||||
|
||||
bool isValid = false;
|
||||
while (!file.eof() && file.is_open()) {
|
||||
getline(file,fileLine);
|
||||
if (!fileLine.empty()) {
|
||||
if (fileLine.compare(fingerprint) == 0) {
|
||||
isValid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
return isValid;
|
||||
}
|
||||
|
||||
ISocketMultiplexerJob*
|
||||
SecureSocket::serviceConnect(ISocketMultiplexerJob* job,
|
||||
bool, bool write, bool error)
|
||||
|
|
|
@ -49,7 +49,7 @@ public:
|
|||
UInt32 secureRead(void* buffer, UInt32 n);
|
||||
UInt32 secureWrite(const void* buffer, UInt32 n);
|
||||
void initSsl(bool server);
|
||||
void loadCertificates(const char* CertFile);
|
||||
bool loadCertificates(String& CertFile);
|
||||
|
||||
private:
|
||||
// SSL
|
||||
|
@ -57,11 +57,15 @@ private:
|
|||
void createSSL();
|
||||
bool secureAccept(int s);
|
||||
bool secureConnect(int s);
|
||||
void showCertificate();
|
||||
bool showCertificate();
|
||||
void checkResult(int n, bool& fatal, bool& retry);
|
||||
void showError();
|
||||
void throwError(const char* reason);
|
||||
void showError(const char* reason = NULL);
|
||||
String getError();
|
||||
void disconnect();
|
||||
void formatFingerprint(String& fingerprint,
|
||||
bool hex = true,
|
||||
bool separator = true);
|
||||
bool verifyCertFingerprint();
|
||||
|
||||
ISocketMultiplexerJob*
|
||||
serviceConnect(ISocketMultiplexerJob*,
|
||||
|
|
|
@ -24,25 +24,27 @@ class ArgsBase {
|
|||
public:
|
||||
ArgsBase();
|
||||
virtual ~ArgsBase();
|
||||
bool m_daemon;
|
||||
bool m_backend;
|
||||
bool m_restartable;
|
||||
bool m_noHooks;
|
||||
const char* m_pname;
|
||||
const char* m_logFilter;
|
||||
const char* m_logFile;
|
||||
const char* m_display;
|
||||
String m_name;
|
||||
bool m_disableTray;
|
||||
bool m_enableIpc;
|
||||
bool m_enableDragDrop;
|
||||
|
||||
public:
|
||||
bool m_daemon;
|
||||
bool m_backend;
|
||||
bool m_restartable;
|
||||
bool m_noHooks;
|
||||
const char* m_pname;
|
||||
const char* m_logFilter;
|
||||
const char* m_logFile;
|
||||
const char* m_display;
|
||||
String m_name;
|
||||
bool m_disableTray;
|
||||
bool m_enableIpc;
|
||||
bool m_enableDragDrop;
|
||||
#if SYSAPI_WIN32
|
||||
bool m_debugServiceWait;
|
||||
bool m_pauseOnExit;
|
||||
bool m_stopOnDeskSwitch;
|
||||
bool m_debugServiceWait;
|
||||
bool m_pauseOnExit;
|
||||
bool m_stopOnDeskSwitch;
|
||||
#endif
|
||||
#if WINAPI_XWINDOWS
|
||||
bool m_disableXInitThreads;
|
||||
bool m_disableXInitThreads;
|
||||
#endif
|
||||
bool m_shouldExit;
|
||||
String m_synergyAddress;
|
||||
|
|
|
@ -331,7 +331,6 @@ ClientApp::handleClientDisconnected(const Event&, void*)
|
|||
updateStatus();
|
||||
}
|
||||
|
||||
|
||||
Client*
|
||||
ClientApp::openClient(const String& name, const NetworkAddress& address,
|
||||
synergy::Screen* screen)
|
||||
|
@ -342,8 +341,7 @@ ClientApp::openClient(const String& name, const NetworkAddress& address,
|
|||
address,
|
||||
new TCPSocketFactory(m_events, getSocketMultiplexer()),
|
||||
screen,
|
||||
args().m_enableDragDrop,
|
||||
args().m_enableCrypto);
|
||||
args());
|
||||
|
||||
try {
|
||||
m_events->adoptHandler(
|
||||
|
|
|
@ -26,5 +26,5 @@ public:
|
|||
ClientArgs();
|
||||
|
||||
public:
|
||||
int m_yscroll;
|
||||
int m_yscroll;
|
||||
};
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
<Feature Id="ProductFeature" Title="$(var.Name)">
|
||||
<ComponentGroupRef Id="ProductComponents" />
|
||||
<ComponentGroupRef Id="OpenSslComponents" />
|
||||
<MergeRef Id="CRT" />
|
||||
</Feature>
|
||||
|
||||
|
@ -61,6 +62,7 @@
|
|||
<Directory Id="$(var.ProgramFilesFolder)">
|
||||
<Directory Id="INSTALLFOLDER" Name="$(var.Name)">
|
||||
<Merge Id="CRT" Language="0" SourceFile="$(var.CRT)" DiskId="1" />
|
||||
<Directory Id="OpenSslDir" Name="OpenSSL" />
|
||||
</Directory>
|
||||
</Directory>
|
||||
<Directory Id="ProgramMenuFolder" />
|
||||
|
@ -68,6 +70,7 @@
|
|||
</Fragment>
|
||||
|
||||
<Fragment>
|
||||
|
||||
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
|
||||
|
||||
<Component Id="Core" Guid="EC9AD3B0-277C-4157-B5C8-5FD5B6A5F4AD">
|
||||
|
@ -112,5 +115,14 @@
|
|||
</Component>
|
||||
|
||||
</ComponentGroup>
|
||||
|
||||
<ComponentGroup Id="OpenSslComponents" Directory="OpenSslDir">
|
||||
<Component Id="OpenSsl" Guid="92648F77-65A6-4B16-AC59-A1F37BD341B1">
|
||||
<File Source="$(var.ExtPath)/openssl/out32dll/libeay32.dll" Id="OpenSslDll1" />
|
||||
<File Source="$(var.ExtPath)/openssl/out32dll/ssleay32.dll" Id="OpenSslDll2" />
|
||||
<File Source="$(var.ExtPath)/openssl/out32dll/openssl.exe" />
|
||||
<File Source="$(var.ResPath)/openssl/synergy.conf" />
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
|
|
|
@ -140,7 +140,11 @@ TEST_F(NetworkTests, sendToClient_mockData)
|
|||
ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape));
|
||||
ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos));
|
||||
|
||||
Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, true, false);
|
||||
|
||||
ClientArgs args;
|
||||
args.m_enableDragDrop = true;
|
||||
args.m_enableCrypto = false;
|
||||
Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, args);
|
||||
|
||||
m_events.adoptHandler(
|
||||
m_events.forIScreen().fileRecieveCompleted(), &client,
|
||||
|
@ -192,7 +196,11 @@ TEST_F(NetworkTests, sendToClient_mockFile)
|
|||
ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape));
|
||||
ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos));
|
||||
|
||||
Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, true, false);
|
||||
|
||||
ClientArgs args;
|
||||
args.m_enableDragDrop = true;
|
||||
args.m_enableCrypto = false;
|
||||
Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, args);
|
||||
|
||||
m_events.adoptHandler(
|
||||
m_events.forIScreen().fileRecieveCompleted(), &client,
|
||||
|
@ -238,7 +246,10 @@ TEST_F(NetworkTests, sendToServer_mockData)
|
|||
ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape));
|
||||
ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos));
|
||||
|
||||
Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, true, false);
|
||||
ClientArgs args;
|
||||
args.m_enableDragDrop = true;
|
||||
args.m_enableCrypto = false;
|
||||
Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, args);
|
||||
|
||||
m_events.adoptHandler(
|
||||
m_events.forClientListener().connected(), &listener,
|
||||
|
@ -290,7 +301,10 @@ TEST_F(NetworkTests, sendToServer_mockFile)
|
|||
ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape));
|
||||
ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos));
|
||||
|
||||
Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, true, false);
|
||||
ClientArgs args;
|
||||
args.m_enableDragDrop = true;
|
||||
args.m_enableCrypto = false;
|
||||
Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, args);
|
||||
|
||||
m_events.adoptHandler(
|
||||
m_events.forClientListener().connected(), &listener,
|
||||
|
|
|
@ -53,3 +53,32 @@ TEST(StringTests, sprintf)
|
|||
|
||||
EXPECT_EQ("answer=42", result);
|
||||
}
|
||||
|
||||
TEST(StringTests, toHex)
|
||||
{
|
||||
String subject = "foobar";
|
||||
int width = 2;
|
||||
|
||||
string::toHex(subject, width);
|
||||
|
||||
EXPECT_EQ("666f6f626172", subject);
|
||||
}
|
||||
|
||||
TEST(StringTests, uppercase)
|
||||
{
|
||||
String subject = "12foo3BaR";
|
||||
|
||||
string::uppercase(subject);
|
||||
|
||||
EXPECT_EQ("12FOO3BAR", subject);
|
||||
}
|
||||
|
||||
TEST(StringTests, removeChar)
|
||||
{
|
||||
String subject = "foobar";
|
||||
const char c = 'o';
|
||||
|
||||
string::removeChar(subject, c);
|
||||
|
||||
EXPECT_EQ("fbar", subject);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue