diff --git a/cmake/Libraries.cmake b/cmake/Libraries.cmake index cb6dc1649..5385ec01a 100644 --- a/cmake/Libraries.cmake +++ b/cmake/Libraries.cmake @@ -45,20 +45,6 @@ macro(configure_libs) endif() find_package(OpenSSL ${REQUIRED_OPENSSL_VERSION} REQUIRED COMPONENTS SSL Crypto) - if(WIN32) #Used for dev in TLS and WIX - cmake_path(SET OPENSSL_ROOT_DIR NORMALIZE "${OPENSSL_INCLUDE_DIR}/..") - message(VERBOSE "Set OPENSSL_ROOT_DIR: ${OPENSSL_ROOT_DIR}") - set(OPENSSL_EXE_DIR "${OPENSSL_ROOT_DIR}/tools/openssl") - add_definitions(-DOPENSSL_EXE_DIR="${OPENSSL_EXE_DIR}") - # HACK Install a copy of openssl on windows - install( - FILES - ${OPENSSL_EXE_DIR}/openssl.exe - ${OPENSSL_EXE_DIR}/openssl.cnf - DESTINATION . - ) - endif() - option(ENABLE_COVERAGE "Enable test coverage" OFF) if(ENABLE_COVERAGE) diff --git a/src/lib/gui/tls/TlsCertificate.cpp b/src/lib/gui/tls/TlsCertificate.cpp index 90ed41192..15a3be826 100644 --- a/src/lib/gui/tls/TlsCertificate.cpp +++ b/src/lib/gui/tls/TlsCertificate.cpp @@ -1,5 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility + * SPDX-FileCopyrightText: (C) 2025 Deskflow Developers * SPDX-FileCopyrightText: (C) 2015 Symless Ltd. * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception */ @@ -17,119 +18,11 @@ static const char *const kCertificateKeyLength = "rsa:"; static const char *const kCertificateHashAlgorithm = "-sha256"; -static const char *const kCertificateLifetime = "365"; - -#if defined(Q_OS_WIN) -static const char *const kWinOpenSslBinary = "openssl.exe"; -static const char *const kConfigFile = "openssl.cnf"; -#elif defined(Q_OS_UNIX) -static const char *const kUnixOpenSslCommand = "openssl"; -#endif - -#if defined(Q_OS_WIN) - -namespace deskflow::gui { - -QString openSslWindowsDir() -{ - - auto openSslDir = QDir(QCoreApplication::applicationDirPath()); - auto openSslBin = QFile(QStringLiteral("%1/%2").arg(openSslDir.absolutePath(), kWinOpenSslBinary)); - - // in production, openssl is deployed with the app. - // in development, we can use the openssl path available at compile-time. - if (!openSslBin.exists()) { - openSslDir = QDir(OPENSSL_EXE_DIR); - } - - // if the path still isn't found, something is seriously wrong. - const auto path = openSslDir.absolutePath(); - if (openSslDir.exists()) { - qDebug("openssl dir: %s", qUtf8Printable(path)); - } else { - qFatal("openssl dir not found: %s", qUtf8Printable(path)); - } - - return QDir::cleanPath(path); -} - -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)) { - qFatal() << "openssl binary not found: " << path; - } - - return path; -} - -} // namespace deskflow::gui - -using namespace deskflow::gui; - -#endif TlsCertificate::TlsCertificate(QObject *parent) : QObject(parent) { } -bool TlsCertificate::runTool(const QStringList &args) -{ -#if defined(Q_OS_WIN) - const auto program = openSslWindowsBinary(); -#else - const auto program = kUnixOpenSslCommand; -#endif - - QStringList environment; - -// Windows is special! :) -// For OpenSSL, it's very common to bundle the openssl.exe and openssl.cnf files -// with the application. This is made a little more complex in the Windows dev -// env, because vcpkg can't find the openssl.cnf file by default, so we need to -// give it a bit of guidance by setting the `OPENSSL_CONF` env var. -#if defined(Q_OS_WIN) - const auto openSslDir = QDir(openSslWindowsDir()); - const auto config = QDir::cleanPath(openSslDir.filePath(kConfigFile)); - environment << QString("OPENSSL_CONF=%1").arg(config); -#endif - - QProcess process; - process.setEnvironment(environment); - for (const auto &envVar : std::as_const(environment)) { - qDebug("set env var: %s", qUtf8Printable(envVar)); - } - - qDebug("running: %s %s", qUtf8Printable(program), qUtf8Printable(args.join(" "))); - process.start(program, args); - bool success = process.waitForStarted(); - - QString toolStderr; - if (success && process.waitForFinished()) { - m_toolStdout = process.readAllStandardOutput().trimmed(); - toolStderr = process.readAllStandardError().trimmed(); - } - - if (int code = process.exitCode(); !success || code != 0) { - qDebug("openssl failed with code %d: %s", code, qUtf8Printable(toolStderr)); - - qCritical("failed to generate tls certificate:\n\n%s", qUtf8Printable(toolStderr)); - return false; - } - - return true; -} - bool TlsCertificate::generateCertificate(const QString &path, int keyLength) { qDebug("generating tls certificate: %s", qUtf8Printable(path)); @@ -143,43 +36,14 @@ bool TlsCertificate::generateCertificate(const QString &path, int keyLength) QString keySize = kCertificateKeyLength + QString::number(keyLength); - QStringList arguments; - - // self signed certificate - arguments.append("req"); - arguments.append("-x509"); - arguments.append("-nodes"); - - // valid duration - arguments.append("-days"); - arguments.append(kCertificateLifetime); - - // subject information - arguments.append("-subj"); - - QString subInfo("/CN=%1"); - arguments.append(subInfo.arg(kAppName)); - - // private key - arguments.append("-newkey"); - arguments.append(keySize); - - // key output filename - arguments.append("-keyout"); - arguments.append(path); - - // certificate output filename - arguments.append("-out"); - arguments.append(path); - - if (runTool(arguments)) { - qDebug("tls certificate generated"); - - return generateFingerprint(path); - } else { - qCritical("failed to generate tls certificate"); + try { + deskflow::generatePemSelfSignedCert(path.toStdString(), keyLength); + } catch (const std::exception &e) { + qCritical() << "failed to generate self-signed pem cert: " << e.what(); return false; } + qDebug("tls certificate generated"); + return generateFingerprint(path); } bool TlsCertificate::generateFingerprint(const QString &certificateFilename) @@ -199,28 +63,5 @@ bool TlsCertificate::generateFingerprint(const QString &certificateFilename) int TlsCertificate::getCertKeyLength(const QString &path) { - - QStringList arguments; - arguments.append("rsa"); - arguments.append("-in"); - arguments.append(path); - arguments.append("-text"); - arguments.append("-noout"); - - if (!runTool(arguments)) { - qFatal("failed to get key length from certificate"); - return 0; - } - - const QString searchStart("Private-Key: ("); - const QString searchEnd(" bit"); - - // Get the line that contains the key length from the output - const auto indexStart = m_toolStdout.indexOf(searchStart); - const auto indexEnd = m_toolStdout.indexOf(searchEnd, indexStart); - const auto start = indexStart + searchStart.length(); - const auto end = indexEnd - (indexStart + searchStart.length()); - auto keyLength = m_toolStdout.mid(start, end); - - return keyLength.toInt(); + return deskflow::getCertLength(path.toStdString()); } diff --git a/src/lib/gui/tls/TlsCertificate.h b/src/lib/gui/tls/TlsCertificate.h index d8afecedf..51857f774 100644 --- a/src/lib/gui/tls/TlsCertificate.h +++ b/src/lib/gui/tls/TlsCertificate.h @@ -1,5 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility + * SPDX-FileCopyrightText: (C) 2025 Deskflow Developers * SPDX-FileCopyrightText: (C) 2015 Symless Ltd. * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception */ @@ -19,9 +20,5 @@ public: int getCertKeyLength(const QString &path); private: - bool runTool(const QStringList &args); bool generateFingerprint(const QString &certificateFilename); - -private: - QString m_toolStdout; }; diff --git a/src/lib/net/SecureUtils.cpp b/src/lib/net/SecureUtils.cpp index eecb4249f..d4683a7a2 100644 --- a/src/lib/net/SecureUtils.cpp +++ b/src/lib/net/SecureUtils.cpp @@ -10,6 +10,7 @@ #include "base/finally.h" #include "io/filesystem.h" +#include #include #include #include @@ -84,4 +85,72 @@ std::vector pemFileCertFingerprint(const std::string &path, Finger return SSLCertFingerprint(cert, type); } + +void generatePemSelfSignedCert(const std::string &path, int keyLength) +{ + auto expirationDays = 365; + + auto *privateKey = EVP_PKEY_new(); + if (!privateKey) { + throw std::runtime_error("could not allocate private key for certificate"); + } + auto privateKeyFree = finally([privateKey]() { EVP_PKEY_free(privateKey); }); + + privateKey = EVP_RSA_gen(keyLength); + + auto *cert = X509_new(); + if (!cert) { + throw std::runtime_error("could not allocate certificate"); + } + auto certFree = finally([cert]() { X509_free(cert); }); + + ASN1_INTEGER_set(X509_get_serialNumber(cert), 1); + X509_gmtime_adj(X509_get_notBefore(cert), 0); + X509_gmtime_adj(X509_get_notAfter(cert), expirationDays * 24 * 3600); + X509_set_pubkey(cert, privateKey); + + auto *name = X509_get_subject_name(cert); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, reinterpret_cast("Deskflow"), -1, -1, 0); + X509_set_issuer_name(cert, name); + + X509_sign(cert, privateKey, EVP_sha256()); + + auto fp = fopenUtf8Path(path.c_str(), "w"); + if (!fp) { + throw std::runtime_error("could not open certificate output path"); + } + auto fileClose = finally([fp]() { std::fclose(fp); }); + + PEM_write_PrivateKey(fp, privateKey, nullptr, nullptr, 0, nullptr, nullptr); + PEM_write_X509(fp, cert); +} + +int getCertLength(const std::string &path) +{ + auto fp = fopenUtf8Path(path.c_str(), "r"); + if (!fp) { + throw std::runtime_error("could not open certificate output path"); + return -1; + } + + EVP_PKEY *privateKey = PEM_read_PrivateKey(fp, NULL, NULL, NULL); + + fclose(fp); + + if (!privateKey) { + throw std::runtime_error("could not open certificate"); + return -1; + } + + if (EVP_PKEY_base_id(privateKey) != EVP_PKEY_RSA) { + throw std::runtime_error("not an RSA key"); + return -1; + } + int size = EVP_PKEY_get_bits(privateKey); + + EVP_PKEY_free(privateKey); + + return size; +} + } // namespace deskflow diff --git a/src/lib/net/SecureUtils.h b/src/lib/net/SecureUtils.h index d64656f77..3cebb6558 100644 --- a/src/lib/net/SecureUtils.h +++ b/src/lib/net/SecureUtils.h @@ -27,4 +27,8 @@ std::string formatSSLFingerprint(const std::vector &fingerprint, bool e std::vector SSLCertFingerprint(X509 *cert, FingerprintType type); std::vector pemFileCertFingerprint(const std::string &path, FingerprintType type); + +void generatePemSelfSignedCert(const std::string &path, int keyLength = 2048); + +int getCertLength(const std::string &path); } // namespace deskflow