Merge pull request #6740 from symless/issue-6669-specify-tls-cert

Added TLS selection capability to GUI
This commit is contained in:
Jnewbon 2020-07-30 10:28:01 +01:00 committed by GitHub
commit c12af2b3ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 351 additions and 62 deletions

View file

@ -73,6 +73,8 @@ const char* AppConfig::m_SynergySettingsName[] = {
"useInternalConfig",
"groupClientChecked",
"serverHostname",
"tlsCertPath",
"tlsKeyLength",
};
static const char* logLevelNames[] =
@ -249,6 +251,13 @@ void AppConfig::loadSettings()
m_ClientGroupChecked = loadSetting(kGroupClientCheck, true).toBool();
m_ServerHostname = loadSetting(kServerHostname).toString();
//Set the default path of the TLS certificate file in the users DIR
QString certificateFilename = QString("%1/%2/%3").arg(m_CoreInterface.getProfileDir(),
"SSL",
"Synergy.pem");
m_TLSCertificatePath = loadSetting(kTLSCertPath, certificateFilename).toString();
m_TLSKeyLength = loadSetting(kTLSKeyLength, "2048").toString();
}
@ -527,3 +536,19 @@ void AppConfig::setSettingModified(T &variable, const T& newValue) {
}
}
void AppConfig::setTLSCertPath(const QString& path) {
m_TLSCertificatePath = path;
}
QString AppConfig::getTLSCertPath() const {
return m_TLSCertificatePath;
}
QString AppConfig::getTLSKeyLength() const {
return m_TLSKeyLength;
}
void AppConfig::setTLSKeyLength(const QString& length) {
m_TLSKeyLength = length;
}

View file

@ -27,6 +27,7 @@
#include <shared/EditionType.h>
#include <mutex>
#include "ConfigBase.h"
#include "CoreInterface.h"
// this should be incremented each time a new page is added. this is
// saved to settings when the user finishes running the wizard. if
@ -126,6 +127,14 @@ class AppConfig: public QObject, public GUI::Config::ConfigBase
bool getClientGroupChecked() const;
QString getServerHostname() const;
/// @brief Gets the current TLS certificate path
/// @return QString The path to the cert
QString getTLSCertPath() const;
/// @brief Get the key length to be used for the private key of a TLS cert
/// @return QString The key length in bits
QString getTLSKeyLength() const;
void setServerGroupChecked(bool);
void setUseExternalConfig(bool) ;
void setConfigFile(const QString&);
@ -133,6 +142,15 @@ class AppConfig: public QObject, public GUI::Config::ConfigBase
void setClientGroupChecked(bool) ;
void setServerHostname(const QString&);
/// @brief Set the path to the TLS/SSL certificate file that will be used
/// @param [in] path The path to the Certificate
void setTLSCertPath(const QString& path);
/// @brief Sets the key length of the private key to use in a TLS connection
/// @param [in] QString length The key length eg: 1024, 2048, 4096
void setTLSKeyLength(const QString& length);
QString lastVersion() const;
void setMinimizeToTray(bool b);
@ -174,6 +192,8 @@ protected:
kUseInternalConfig,
kGroupClientCheck,
kServerHostname,
kTLSCertPath,
kTLSKeyLength,
};
void setScreenName(const QString& s);
@ -224,10 +244,15 @@ protected:
bool m_ClientGroupChecked;
QString m_ServerHostname;
QString m_TLSCertificatePath; /// @brief The path to the TLS certificate file
QString m_TLSKeyLength; /// @brief The key length of the TLS cert to make
bool m_LoadFromSystemScope; /// @brief should the setting be loaded from SystemScope
/// If the user has settings but this is true then
/// system settings will be loaded instead of the users
CoreInterface m_CoreInterface;
static const char m_SynergysName[];
static const char m_SynergycName[];
static const char m_SynergyLogDir[];

View file

@ -670,6 +670,7 @@ void MainWindow::startSynergy()
if (m_AppConfig->getCryptoEnabled()) {
args << "--enable-crypto";
args << "--tls-cert" << m_AppConfig->getTLSCertPath();
}
#if defined(Q_OS_WIN)
@ -744,10 +745,6 @@ void MainWindow::retryStart()
void
MainWindow::sslToggled (bool enabled)
{
if (enabled) {
m_pSslCertificate = new SslCertificate(this);
m_pSslCertificate->generateCertificate();
}
updateLocalFingerprint();
}

View file

@ -65,9 +65,22 @@ void SettingsDialog::accept()
appConfig().setAutoHide(m_pCheckBoxAutoHide->isChecked());
appConfig().setAutoConfig(m_pCheckBoxAutoConfig->isChecked());
appConfig().setMinimizeToTray(m_pCheckBoxMinimizeToTray->isChecked());
appConfig().setTLSCertPath(m_pLineEditCertificatePath->text());
bool keyLengthChanged = appConfig().getTLSKeyLength() != m_pComboBoxKeyLength->currentText();
appConfig().setTLSKeyLength(m_pComboBoxKeyLength->currentText());
//We only need to test the System scoped Radio as they are connected
appConfig().setLoadFromSystemScope(m_pRadioSystemScope->isChecked());
if(m_pCheckBoxEnableCrypto->isChecked()) {
SslCertificate sslCertificate;
sslCertificate.generateCertificate(appConfig().getTLSCertPath(),
m_pComboBoxKeyLength->currentText(),
keyLengthChanged);
}
m_appConfig.setCryptoEnabled(m_pCheckBoxEnableCrypto->isChecked());
QDialog::accept();
}
@ -115,8 +128,17 @@ void SettingsDialog::loadFromConfig() {
setIndexFromItemData(m_pComboLanguage, appConfig().language());
m_pCheckBoxAutoHide->setChecked(appConfig().getAutoHide());
m_pCheckBoxMinimizeToTray->setChecked(appConfig().getMinimizeToTray());
m_pLineEditCertificatePath->setText(appConfig().getTLSCertPath());
m_pCheckBoxEnableCrypto->setChecked(m_appConfig.getCryptoEnabled());
//If the tls file exists test its key length
if (QFile(appConfig().getTLSCertPath()).exists()) {
updateKeyLengthOnFile(appConfig().getTLSCertPath());
} else {
m_pComboBoxKeyLength->setCurrentIndex(m_pComboBoxKeyLength->findText(appConfig().getTLSKeyLength()));
}
if (m_appConfig.isSystemScoped()) {
m_pRadioSystemScope->setChecked(true);
}
@ -142,6 +164,8 @@ void SettingsDialog::loadFromConfig() {
#endif
m_pCheckBoxEnableCrypto->setChecked(m_appConfig.getCryptoEnabled());
m_pGroupBoxTLS->setVisible(m_appConfig.getCryptoEnabled());
#ifdef SYNERGY_ENTERPRISE
@ -160,6 +184,7 @@ void SettingsDialog::loadFromConfig() {
m_pCheckBoxAutoConfig->setChecked(appConfig().autoConfig());
#endif
adjustSize();
}
@ -202,9 +227,13 @@ void SettingsDialog::on_m_pCheckBoxEnableCrypto_toggled(bool checked)
m_appConfig.setCryptoEnabled(checked);
if (checked) {
SslCertificate sslCertificate;
sslCertificate.generateCertificate();
sslCertificate.generateCertificate(m_pLineEditCertificatePath->text(), m_pComboBoxKeyLength->currentText());
m_pMainWindow->updateLocalFingerprint();
verticalSpacer_4->changeSize(10, 10, QSizePolicy::Minimum);
} else {
verticalSpacer_4->changeSize(10, 0, QSizePolicy::Ignored);
}
adjustSize();
}
void SettingsDialog::on_m_pLabelInstallBonjour_linkActivated(const QString&)
@ -219,3 +248,57 @@ void SettingsDialog::on_m_pRadioSystemScope_toggled(bool checked)
appConfig().setLoadFromSystemScope(checked);
loadFromConfig();
}
void SettingsDialog::on_m_pPushButtonBrowseCert_clicked() {
QString fileName = QFileDialog::getSaveFileName(
this, tr("Select a TLS certificate to use..."),
m_pLineEditCertificatePath->text(),
"Cert (*.pem)",
nullptr,
QFileDialog::DontConfirmOverwrite);
if (!fileName.isEmpty()) {
m_pLineEditCertificatePath->setText(fileName);
//If the tls file exists test its key length and update
if (QFile(appConfig().getTLSCertPath()).exists()) {
updateKeyLengthOnFile(fileName);
}
}
updateRegenButton();
}
void SettingsDialog::regenerateSSLCert() {
SslCertificate sslCertificate;
sslCertificate.generateCertificate(appConfig().getTLSCertPath(),
appConfig().getTLSKeyLength(),
true);
m_pMainWindow->updateLocalFingerprint();
}
void SettingsDialog::on_m_pComboBoxKeyLength_currentIndexChanged(int index) {
updateRegenButton();
}
void SettingsDialog::updateRegenButton() {
// Disable the Regenerate cert button if the key length is different to saved
auto keyChanged = appConfig().getTLSKeyLength() != m_pComboBoxKeyLength->currentText();
auto pathChanged = appConfig().getTLSCertPath() != m_pLineEditCertificatePath->text();
auto cryptoChanged = appConfig().getCryptoEnabled() != m_pCheckBoxEnableCrypto->isChecked();
//NOR the above bools, if any have changed regen should be disabled as it will be done on save
auto nor = !(keyChanged || pathChanged || cryptoChanged);
m_pPushButtonRegenCert->setEnabled(nor);
}
void SettingsDialog::on_m_pPushButtonRegenCert_clicked() {
regenerateSSLCert();
}
void SettingsDialog::updateKeyLengthOnFile(const QString &path) {
SslCertificate ssl;
auto length = ssl.getCertKeyLength(path);
auto index = m_pComboBoxKeyLength->findText(length);
m_pComboBoxKeyLength->setCurrentIndex(index);
//Also update what is in the appconfig to match the file itself
appConfig().setTLSKeyLength(length);
}

View file

@ -48,6 +48,16 @@ class SettingsDialog : public QDialog, public Ui::SettingsDialogBase
/// @brief Causes the dialog to load all the settings from m_appConfig
void loadFromConfig();
/// @brief Forces the regeneration of the TLS cert from the saved settings
void regenerateSSLCert();
/// @brief Check if the regenerate button should be enabled or disabled and sets it
void updateRegenButton();
/// @brief Updates the key length value based on the loaded file
/// @param [in] QString path The path to the file to test
void updateKeyLengthOnFile(const QString& path);
private:
MainWindow* m_pMainWindow;
AppConfig& m_appConfig;
@ -65,6 +75,17 @@ class SettingsDialog : public QDialog, public Ui::SettingsDialogBase
/// @brief Handles the toggling of the system scoped radio button
/// As the user scope radio is connected this will fire for either radio button
void on_m_pRadioSystemScope_toggled(bool checked);
/// @brief Handles the click event of the Cert Path browse button
/// displaying a file browser
void on_m_pPushButtonBrowseCert_clicked();
/// @brief Handles the TLS cert key length changed event
void on_m_pComboBoxKeyLength_currentIndexChanged(int index);
/// @brief handels the regenerate cert button event
/// This will regenerate the TLS certificate as long as the settings haven't changed
void on_m_pPushButtonRegenCert_clicked();
};
#endif

View file

@ -6,16 +6,15 @@
<rect>
<x>0</x>
<y>0</y>
<width>357</width>
<height>496</height>
<width>378</width>
<height>756</height>
</rect>
</property>
<property name="windowTitle">
<string>Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="QGroupBox" name="m_pGroupScope">
<property name="title">
<string>&amp;Settings Scope</string>
@ -41,7 +40,7 @@
</layout>
</widget>
</item>
<item>
<item row="1" column="0">
<widget class="QGroupBox" name="m_pGroupAdvanced">
<property name="title">
<string>&amp;Miscellaneous</string>
@ -190,7 +189,7 @@
</layout>
</widget>
</item>
<item>
<item row="2" column="0">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -206,7 +205,7 @@
</property>
</spacer>
</item>
<item>
<item row="3" column="0">
<widget class="QGroupBox" name="m_pGroupNetwork">
<property name="enabled">
<bool>true</bool>
@ -220,10 +219,7 @@
<property name="title">
<string>&amp;Network</string>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<property name="leftMargin">
<number>2</number>
</property>
@ -236,7 +232,7 @@
<property name="bottomMargin">
<number>12</number>
</property>
<item row="0" column="1">
<item row="0" column="0">
<layout class="QGridLayout" name="m_pGridLayoutNetwork">
<property name="leftMargin">
<number>0</number>
@ -244,36 +240,6 @@
<property name="verticalSpacing">
<number>12</number>
</property>
<item row="1" column="0">
<widget class="QCheckBox" name="m_pCheckBoxEnableCrypto">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Enable &amp;TLS Encryption</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="m_pCheckBoxAutoConfig">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Enable Auto Config</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="m_pLabelInstallBonjour">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;#&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#007af4;&quot;&gt;Install Bonjour&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="m_pLabelProUpgrade">
<property name="text">
@ -287,12 +253,42 @@
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="m_pLabelInstallBonjour">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;#&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#007af4;&quot;&gt;Install Bonjour&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="m_pCheckBoxAutoConfig">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Enable Auto Config</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="m_pCheckBoxEnableCrypto">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Enable &amp;TLS Encryption</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<item row="4" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -308,7 +304,85 @@
</property>
</spacer>
</item>
<item>
<item row="5" column="0">
<widget class="QGroupBox" name="m_pGroupBoxTLS">
<property name="title">
<string>TLS/SSL Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1">
<widget class="QLineEdit" name="m_pLineEditCertificatePath"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="m_pLabel_29">
<property name="text">
<string>Key length</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="m_pLabel_30">
<property name="text">
<string>Certificate Path</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="m_pPushButtonBrowseCert">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="m_pComboBoxKeyLength">
<property name="currentText">
<string>2048</string>
</property>
<item>
<property name="text">
<string>1024</string>
</property>
</item>
<item>
<property name="text">
<string>2048</string>
</property>
</item>
<item>
<property name="text">
<string>4096</string>
</property>
</item>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="m_pPushButtonRegenCert">
<property name="text">
<string>Regenerate Cert</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="6" column="0">
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item row="7" column="0">
<widget class="QGroupBox" name="m_pGroupLog">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@ -393,7 +467,7 @@
</layout>
</widget>
</item>
<item>
<item row="8" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -409,7 +483,7 @@
</property>
</spacer>
</item>
<item>
<item row="9" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -422,13 +496,21 @@
</layout>
</widget>
<tabstops>
<tabstop>m_pRadioSystemScope</tabstop>
<tabstop>m_pRadioUserScope</tabstop>
<tabstop>m_pComboLanguage</tabstop>
<tabstop>m_pLineEditScreenName</tabstop>
<tabstop>m_pSpinBoxPort</tabstop>
<tabstop>m_pLineEditInterface</tabstop>
<tabstop>m_pCheckBoxMinimizeToTray</tabstop>
<tabstop>m_pComboElevate</tabstop>
<tabstop>m_pCheckBoxAutoHide</tabstop>
<tabstop>m_pCheckBoxMinimizeToTray</tabstop>
<tabstop>m_pCheckBoxAutoConfig</tabstop>
<tabstop>m_pCheckBoxEnableCrypto</tabstop>
<tabstop>m_pComboBoxKeyLength</tabstop>
<tabstop>m_pLineEditCertificatePath</tabstop>
<tabstop>m_pPushButtonBrowseCert</tabstop>
<tabstop>m_pPushButtonRegenCert</tabstop>
<tabstop>m_pComboLogLevel</tabstop>
<tabstop>m_pCheckBoxLogToFile</tabstop>
<tabstop>m_pLineEditLogFilename</tabstop>
@ -468,5 +550,21 @@
</hint>
</hints>
</connection>
<connection>
<sender>m_pCheckBoxEnableCrypto</sender>
<signal>toggled(bool)</signal>
<receiver>m_pGroupBoxTLS</receiver>
<slot>setVisible(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>100</x>
<y>413</y>
</hint>
<hint type="destinationlabel">
<x>188</x>
<y>508</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -25,7 +25,7 @@
static const char kCertificateKeyLength[] = "rsa:2048"; //RSA Bit length (e.g. 1024/2048/4096)
static const char kCertificateKeyLength[] = "rsa:"; //RSA Bit length (e.g. 1024/2048/4096)
static const char kCertificateHashAlgorithm[] = "-sha256"; //fingerprint hashing algorithm
static const char kCertificateLifetime[] = "365";
static const char kCertificateSubjectInfo[] = "/CN=Synergy";
@ -93,7 +93,7 @@ bool SslCertificate::runTool(const QStringList& args)
return true;
}
void SslCertificate::generateCertificate()
void SslCertificate::generateCertificate(const QString& path, const QString& keyLength, bool forceGen)
{
QString sslDirPath = QString("%1%2%3")
.arg(m_ProfileDir)
@ -105,8 +105,13 @@ void SslCertificate::generateCertificate()
.arg(QDir::separator())
.arg(kCertificateFilename);
QFile file(filename);
if (!file.exists()) {
QString keySize = kCertificateKeyLength + keyLength;
const QString pathToUse = path.isEmpty() ? filename : path;
//If path is empty use filename
QFile file(pathToUse);
if (!file.exists() || forceGen) {
QStringList arguments;
// self signed certificate
@ -126,7 +131,7 @@ void SslCertificate::generateCertificate()
// private key
arguments.append("-newkey");
arguments.append(kCertificateKeyLength);
arguments.append(keySize);
QDir sslDir(sslDirPath);
if (!sslDir.exists()) {
@ -135,11 +140,11 @@ void SslCertificate::generateCertificate()
// key output filename
arguments.append("-keyout");
arguments.append(filename);
arguments.append(pathToUse);
// certificate output filename
arguments.append("-out");
arguments.append(filename);
arguments.append(pathToUse);
if (!runTool(arguments)) {
return;
@ -148,7 +153,7 @@ void SslCertificate::generateCertificate()
emit info(tr("SSL certificate generated."));
}
generateFingerprint(filename);
generateFingerprint(pathToUse);
emit generateFinished();
}
@ -181,3 +186,28 @@ void SslCertificate::generateFingerprint(const QString& certificateFilename)
emit error(tr("Failed to find SSL fingerprint."));
}
}
QString SslCertificate::getCertKeyLength(const QString &path) {
QStringList arguments;
arguments.append("rsa");
arguments.append("-in");
arguments.append(path);
arguments.append("-text");
arguments.append("-noout");
if (!runTool(arguments)) {
return QString();
}
const QString searchStart("Private-Key: (");
const QString searchEnd(" bit");
//Get the line that contains the key length from the output
const auto indexStart = m_ToolOutput.indexOf(searchStart);
const auto indexEnd = m_ToolOutput.indexOf(searchEnd, indexStart);
const auto start = indexStart + searchStart.length();
const auto end = indexEnd - (indexStart + searchStart.length());
auto keyLength = m_ToolOutput.mid(start, end);
return keyLength;
}

View file

@ -20,6 +20,7 @@
#include "CoreInterface.h"
#include <QObject>
#include <base/String.h>
class SslCertificate : public QObject
{
@ -29,7 +30,16 @@ public:
explicit SslCertificate(QObject *parent = 0);
public slots:
void generateCertificate();
/// @brief Generates a TLS cert and private key
/// @param [in] QString path The path of the file to be generated
/// @param [in] QString keyLength The size of the private key. default: 2048
/// @param [in] bool Should the file be created regardless of if the file already exists
void generateCertificate(const QString& path = QString(), const QString& keyLength = "2048", bool forceGen = false);
/// @brief Get the key length of a TLS private key
/// @param [in] QString path The path of the file to checked
/// @return QString The key legnth as a string
QString getCertKeyLength(const QString& path);
signals:
void error(QString e);