2015-04-20 17:51:22 +00:00
|
|
|
/*
|
|
|
|
|
* synergy -- mouse and keyboard sharing utility
|
2024-07-26 22:53:52 +00:00
|
|
|
* Copyright (C) 2015 Symless Ltd.
|
2015-04-20 17:51:22 +00:00
|
|
|
*
|
|
|
|
|
* This package is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU General Public License
|
2015-05-03 02:33:52 +00:00
|
|
|
* found in the file LICENSE that should have accompanied this file.
|
2015-04-20 17:51:22 +00:00
|
|
|
*
|
|
|
|
|
* 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/>.
|
|
|
|
|
*/
|
|
|
|
|
|
2024-07-26 22:53:52 +00:00
|
|
|
#include "TlsCertificate.h"
|
2015-04-20 17:51:22 +00:00
|
|
|
|
2024-07-26 22:53:52 +00:00
|
|
|
#include "TlsFingerprint.h"
|
2015-04-20 17:51:22 +00:00
|
|
|
|
|
|
|
|
#include <QCoreApplication>
|
2024-07-02 19:07:06 +00:00
|
|
|
#include <QDir>
|
|
|
|
|
#include <QProcess>
|
2024-08-07 14:05:18 +00:00
|
|
|
#include <optional>
|
2015-04-20 17:51:22 +00:00
|
|
|
|
2024-07-22 16:48:02 +00:00
|
|
|
static const char *const kCertificateKeyLength = "rsa:";
|
|
|
|
|
static const char *const kCertificateHashAlgorithm = "-sha256";
|
|
|
|
|
static const char *const kCertificateLifetime = "365";
|
|
|
|
|
static const char *const kCertificateSubjectInfo = "/CN=Synergy";
|
|
|
|
|
static const char *const kCertificateFilename = "Synergy.pem";
|
|
|
|
|
static const char *const kSslDir = "SSL";
|
2015-04-20 17:51:22 +00:00
|
|
|
|
|
|
|
|
#if defined(Q_OS_WIN)
|
2024-07-22 16:48:02 +00:00
|
|
|
static const char *const kWinOpenSslDir = "OpenSSL";
|
|
|
|
|
static const char *const kWinOpenSslBinary = "openssl.exe";
|
|
|
|
|
static const char *const kConfigFile = "synergy.conf";
|
|
|
|
|
#elif defined(Q_OS_UNIX)
|
|
|
|
|
static const char *const kUnixOpenSslCommand = "openssl";
|
2015-04-20 17:51:22 +00:00
|
|
|
#endif
|
|
|
|
|
|
2024-07-22 16:48:02 +00:00
|
|
|
#if defined(Q_OS_WIN)
|
|
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
namespace synergy::gui {
|
|
|
|
|
|
2024-07-22 16:48:02 +00:00
|
|
|
QString openSslWindowsDir() {
|
|
|
|
|
|
|
|
|
|
auto appDir = QDir(QCoreApplication::applicationDirPath());
|
|
|
|
|
auto openSslDir = QDir(appDir.filePath(kWinOpenSslDir));
|
|
|
|
|
|
|
|
|
|
// in production, openssl is deployed with the app.
|
|
|
|
|
// in development, we can use the openssl path available at compile-time.
|
|
|
|
|
if (!openSslDir.exists()) {
|
|
|
|
|
openSslDir = QDir(OPENSSL_PATH);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if the path still isn't found, something is seriously wrong.
|
|
|
|
|
if (!openSslDir.exists()) {
|
2024-08-01 00:13:01 +00:00
|
|
|
qFatal() << "openssl dir not found: " << openSslDir;
|
2024-07-22 16:48:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return QDir::cleanPath(openSslDir.absolutePath());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString openSslWindowsBinary() {
|
|
|
|
|
auto dir = QDir(openSslWindowsDir());
|
|
|
|
|
auto path = dir.filePath(kWinOpenSslBinary);
|
|
|
|
|
|
|
|
|
|
// when installed, there is no openssl bin dir; it's installed at the base.
|
|
|
|
|
// in development, we use the standard dir structure for openssl (bin dir).
|
|
|
|
|
if (!QFile::exists(path)) {
|
|
|
|
|
auto binDir = QDir(dir.filePath("bin"));
|
|
|
|
|
path = binDir.filePath(kWinOpenSslBinary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if the path still isn't found, something is seriously wrong.
|
|
|
|
|
if (!QFile::exists(path)) {
|
2024-08-01 00:13:01 +00:00
|
|
|
qFatal() << "openssl binary not found: " << path;
|
2024-07-22 16:48:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace synergy::gui
|
|
|
|
|
|
|
|
|
|
using namespace synergy::gui;
|
|
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
#endif
|
|
|
|
|
|
2024-07-26 22:53:52 +00:00
|
|
|
TlsCertificate::TlsCertificate(QObject *parent) : QObject(parent) {
|
2024-08-07 14:05:18 +00:00
|
|
|
m_profileDir = m_coreTool.getProfileDir();
|
|
|
|
|
if (m_profileDir.isEmpty()) {
|
|
|
|
|
qCritical("empty profile directory result");
|
2024-07-02 19:07:06 +00:00
|
|
|
}
|
2015-04-20 17:51:22 +00:00
|
|
|
}
|
|
|
|
|
|
2024-07-26 22:53:52 +00:00
|
|
|
bool TlsCertificate::runTool(const QStringList &args) {
|
2024-07-02 19:07:06 +00:00
|
|
|
QString program;
|
2015-04-20 17:51:22 +00:00
|
|
|
#if defined(Q_OS_WIN)
|
2024-07-22 16:48:02 +00:00
|
|
|
program = openSslWindowsBinary();
|
2015-04-20 17:51:22 +00:00
|
|
|
#else
|
2024-07-02 19:07:06 +00:00
|
|
|
program = kUnixOpenSslCommand;
|
2015-04-20 17:51:22 +00:00
|
|
|
#endif
|
|
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
QStringList environment;
|
2015-04-20 17:51:22 +00:00
|
|
|
#if defined(Q_OS_WIN)
|
2024-07-22 16:48:02 +00:00
|
|
|
auto openSslDir = QDir(openSslWindowsDir());
|
|
|
|
|
auto config = QDir::cleanPath(openSslDir.filePath(kConfigFile));
|
2024-08-07 14:05:18 +00:00
|
|
|
if (!QFile::exists(config)) {
|
|
|
|
|
qDebug("openssl config file not found: %s", qUtf8Printable(config));
|
|
|
|
|
|
|
|
|
|
// if the expected production file location doesn't exist, try the dev path.
|
|
|
|
|
config = QDir::cleanPath(QString("res/openssl/%1").arg(kConfigFile));
|
|
|
|
|
|
|
|
|
|
// if it still isn't there, then there's something seriously wrong.
|
|
|
|
|
if (!QFile::exists(config)) {
|
|
|
|
|
qFatal() << "openssl config file not found: " << config;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-22 16:48:02 +00:00
|
|
|
environment << QString("OPENSSL_CONF=%1").arg(config);
|
2015-04-20 17:51:22 +00:00
|
|
|
#endif
|
|
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
qDebug(
|
|
|
|
|
"running: %s %s", qUtf8Printable(program),
|
|
|
|
|
qUtf8Printable(args.join(" ")));
|
|
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
QProcess process;
|
2024-08-07 14:05:18 +00:00
|
|
|
|
|
|
|
|
for (const auto &envVar : environment) {
|
|
|
|
|
qDebug("setting env var %s", qUtf8Printable(envVar));
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
process.setEnvironment(environment);
|
2024-08-07 14:05:18 +00:00
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
process.start(program, args);
|
2016-12-28 11:50:32 +00:00
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
bool success = process.waitForStarted();
|
2016-12-28 11:50:32 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
QString stderrOutput;
|
2024-07-02 19:07:06 +00:00
|
|
|
if (success && process.waitForFinished()) {
|
2024-08-07 14:05:18 +00:00
|
|
|
m_toolStdout = process.readAllStandardOutput().trimmed();
|
|
|
|
|
stderrOutput = process.readAllStandardError().trimmed();
|
2024-07-02 19:07:06 +00:00
|
|
|
}
|
2016-12-28 11:50:32 +00:00
|
|
|
|
2024-07-22 16:48:02 +00:00
|
|
|
if (int code = process.exitCode(); !success || code != 0) {
|
2024-08-07 14:05:18 +00:00
|
|
|
qDebug(
|
|
|
|
|
"openssl failed with code %d: %s", code, qUtf8Printable(stderrOutput));
|
|
|
|
|
|
|
|
|
|
qCritical(
|
|
|
|
|
"failed to generate TLS certificate:\n\n%s",
|
|
|
|
|
qUtf8Printable(stderrOutput));
|
2024-07-02 19:07:06 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
2019-08-02 12:11:47 +00:00
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
return true;
|
2015-04-21 11:55:45 +00:00
|
|
|
}
|
2015-04-20 17:51:22 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
bool TlsCertificate::generateCertificate(const QString &path, int keyLength) {
|
2024-07-02 19:07:06 +00:00
|
|
|
QString sslDirPath =
|
2024-08-07 14:05:18 +00:00
|
|
|
QString("%1%2%3").arg(m_profileDir).arg(QDir::separator()).arg(kSslDir);
|
2020-07-28 12:00:20 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
QString defaultPath = QString("%1%2%3")
|
|
|
|
|
.arg(sslDirPath)
|
|
|
|
|
.arg(QDir::separator())
|
|
|
|
|
.arg(kCertificateFilename);
|
2015-04-20 17:51:22 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
QString keySize = kCertificateKeyLength + QString::number(keyLength);
|
2015-04-20 17:51:22 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
const QString pathToUse =
|
|
|
|
|
QDir::cleanPath(path.isEmpty() ? defaultPath : path);
|
2016-09-30 16:31:55 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
qDebug("generating tls certificate: %s", qUtf8Printable(pathToUse));
|
2016-09-30 16:31:55 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
QStringList arguments;
|
2015-04-20 17:51:22 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
// self signed certificate
|
|
|
|
|
arguments.append("req");
|
|
|
|
|
arguments.append("-x509");
|
|
|
|
|
arguments.append("-nodes");
|
2016-09-30 16:31:55 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
// valide duration
|
|
|
|
|
arguments.append("-days");
|
|
|
|
|
arguments.append(kCertificateLifetime);
|
2016-09-30 16:31:55 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
// subject information
|
|
|
|
|
arguments.append("-subj");
|
2016-09-30 16:31:55 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
QString subInfo(kCertificateSubjectInfo);
|
|
|
|
|
arguments.append(subInfo);
|
2016-09-30 16:31:55 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
// private key
|
|
|
|
|
arguments.append("-newkey");
|
|
|
|
|
arguments.append(keySize);
|
2015-04-20 17:51:22 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
if (QDir sslDir(sslDirPath); !sslDir.exists()) {
|
|
|
|
|
sslDir.mkpath(".");
|
|
|
|
|
}
|
2015-04-20 17:51:22 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
// key output filename
|
|
|
|
|
arguments.append("-keyout");
|
|
|
|
|
arguments.append(pathToUse);
|
2016-12-28 11:50:32 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
// certificate output filename
|
|
|
|
|
arguments.append("-out");
|
|
|
|
|
arguments.append(pathToUse);
|
2016-12-28 11:50:32 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
if (runTool(arguments)) {
|
|
|
|
|
qDebug("tls certificate generated");
|
2016-12-28 11:50:32 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
return generateFingerprint(pathToUse);
|
|
|
|
|
} else {
|
|
|
|
|
qCritical("failed to generate tls certificate");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2015-04-20 17:51:22 +00:00
|
|
|
}
|
2020-07-28 12:00:20 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
bool TlsCertificate::generateFingerprint(const QString &certificateFilename) {
|
|
|
|
|
qDebug("generating tls fingerprint");
|
|
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
QStringList arguments;
|
|
|
|
|
arguments.append("x509");
|
|
|
|
|
arguments.append("-fingerprint");
|
|
|
|
|
arguments.append(kCertificateHashAlgorithm);
|
|
|
|
|
arguments.append("-noout");
|
|
|
|
|
arguments.append("-in");
|
|
|
|
|
arguments.append(certificateFilename);
|
|
|
|
|
|
|
|
|
|
if (!runTool(arguments)) {
|
2024-08-07 14:05:18 +00:00
|
|
|
qCritical("failed to generate tls fingerprint");
|
|
|
|
|
return false;
|
2024-07-02 19:07:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// find the fingerprint from the tool output
|
2024-08-07 14:05:18 +00:00
|
|
|
auto i = m_toolStdout.indexOf("=");
|
2024-07-02 19:07:06 +00:00
|
|
|
if (i != -1) {
|
|
|
|
|
i++;
|
2024-08-07 14:05:18 +00:00
|
|
|
QString fingerprint = m_toolStdout.mid(i, m_toolStdout.size() - i);
|
2024-07-02 19:07:06 +00:00
|
|
|
|
2024-07-26 22:53:52 +00:00
|
|
|
TlsFingerprint::local().trust(fingerprint, false);
|
2024-08-07 14:05:18 +00:00
|
|
|
qDebug("tls fingerprint generated");
|
|
|
|
|
return true;
|
2024-07-02 19:07:06 +00:00
|
|
|
} else {
|
2024-08-07 14:05:18 +00:00
|
|
|
qCritical("failed to find tls fingerprint in tls tool output");
|
|
|
|
|
return false;
|
2024-07-02 19:07:06 +00:00
|
|
|
}
|
|
|
|
|
}
|
2020-07-28 12:00:20 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
int TlsCertificate::getCertKeyLength(const QString &path) {
|
2020-07-28 12:00:20 +00:00
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
QStringList arguments;
|
|
|
|
|
arguments.append("rsa");
|
|
|
|
|
arguments.append("-in");
|
|
|
|
|
arguments.append(path);
|
|
|
|
|
arguments.append("-text");
|
|
|
|
|
arguments.append("-noout");
|
|
|
|
|
|
|
|
|
|
if (!runTool(arguments)) {
|
2024-08-07 14:05:18 +00:00
|
|
|
qFatal("failed to get key length from certificate");
|
|
|
|
|
return 0;
|
2024-07-02 19:07:06 +00:00
|
|
|
}
|
2024-08-07 14:05:18 +00:00
|
|
|
|
2024-07-02 19:07:06 +00:00
|
|
|
const QString searchStart("Private-Key: (");
|
|
|
|
|
const QString searchEnd(" bit");
|
|
|
|
|
|
|
|
|
|
// Get the line that contains the key length from the output
|
2024-08-07 14:05:18 +00:00
|
|
|
const auto indexStart = m_toolStdout.indexOf(searchStart);
|
|
|
|
|
const auto indexEnd = m_toolStdout.indexOf(searchEnd, indexStart);
|
2024-07-02 19:07:06 +00:00
|
|
|
const auto start = indexStart + searchStart.length();
|
|
|
|
|
const auto end = indexEnd - (indexStart + searchStart.length());
|
2024-08-07 14:05:18 +00:00
|
|
|
auto keyLength = m_toolStdout.mid(start, end);
|
2024-07-02 19:07:06 +00:00
|
|
|
|
2024-08-07 14:05:18 +00:00
|
|
|
return keyLength.toInt();
|
2020-07-28 12:00:20 +00:00
|
|
|
}
|