diff --git a/src/apps/deskflow-gui/MainWindow.cpp b/src/apps/deskflow-gui/MainWindow.cpp index ffeca7fdd..959d6f758 100644 --- a/src/apps/deskflow-gui/MainWindow.cpp +++ b/src/apps/deskflow-gui/MainWindow.cpp @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * SPDX-FileCopyrightText: (C) 2024 Chris Rizzitello + * SPDX-FileCopyrightText: (C) 2024 - 2025 Chris Rizzitello * SPDX-FileCopyrightText: (C) 2012 - 2024 Symless Ltd. * SPDX-FileCopyrightText: (C) 2008 Volker Lanz * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception @@ -13,6 +13,7 @@ #include "dialogs/ServerConfigDialog.h" #include "dialogs/SettingsDialog.h" +#include "base/String.h" #include "common/constants.h" #include "gui/Logger.h" #include "gui/config/ConfigScopes.h" @@ -23,7 +24,9 @@ #include "gui/string_utils.h" #include "gui/style_utils.h" #include "gui/styles.h" -#include "gui/tls/TlsFingerprint.h" +#include "net/FingerprintDatabase.h" +#include "net/SecureUtils.h" + #include "platform/wayland.h" #if defined(Q_OS_LINUX) @@ -466,7 +469,29 @@ void MainWindow::updateSize() void MainWindow::showMyFingerprint() { - QMessageBox::information(this, "TLS fingerprint", TlsFingerprint::local().readFirst()); + auto localPath = QStringLiteral("%1/%2").arg(getTlsPath(), kFingerprintLocalFilename).toStdString(); + if (!QFile::exists(QString::fromStdString(localPath))) { + QMessageBox::information( + this, tr("TLS fingerprint Error"), + tr("Unable to read localfinger print: %1\n You may want to regenerate your keys.") + .arg(QString::fromStdString(localPath)) + ); + return; + } + + deskflow::FingerprintDatabase db; + db.read(localPath); + if (db.fingerprints().empty()) { + QMessageBox::information( + this, tr("TLS fingerprint Error"), + tr("Unable to read localDatabase\n You may want to regenerate your keys.") + .arg(QString::fromStdString(localPath)) + ); + return; + } + + const auto fingerprint = QString::fromStdString(deskflow::formatSSLFingerprint(db.fingerprints().front().data)); + QMessageBox::information(this, "TLS fingerprint", fingerprint); } void MainWindow::setModeServer() @@ -669,8 +694,13 @@ void MainWindow::checkFingerprint(const QString &line) return; } - auto fingerprint = match.captured(1); - if (TlsFingerprint::trustedServers().isTrusted(fingerprint)) { + auto localPath = QStringLiteral("%1/%2").arg(getTlsPath(), kFingerprintTrustedServersFilename).toStdString(); + + deskflow::FingerprintDatabase db; + db.read(localPath); + + const deskflow::FingerprintData fingerprint{"sha1", deskflow::string::fromHex(match.captured(1).toStdString())}; + if (db.isTrusted(fingerprint)) { return; } @@ -690,13 +720,13 @@ void MainWindow::checkFingerprint(const QString &line) "you're expecting (it could be a malicious user).

" "

Do you want to trust this fingerprint for future " "connections? If you don't, a connection cannot be made.

") - .arg(fingerprint), + .arg(QString::fromStdString(deskflow::formatSSLFingerprint(fingerprint.data))), QMessageBox::Yes | QMessageBox::No ); if (fingerprintReply == QMessageBox::Yes) { - // start core process again after trusting fingerprint. - TlsFingerprint::trustedServers().trust(fingerprint); + db.addTrusted(fingerprint); + db.write(localPath); m_coreProcess.start(); } @@ -897,7 +927,8 @@ void MainWindow::updateLocalFingerprint() { bool fingerprintExists = false; try { - fingerprintExists = TlsFingerprint::local().fileExists(); + auto localPath = QStringLiteral("%1/%2").arg(getTlsPath(), kFingerprintLocalFilename).toStdString(); + fingerprintExists = QFile::exists(QString::fromStdString(localPath)); } catch (const std::exception &e) { qDebug() << e.what(); qFatal() << "failed to check if fingerprint exists"; @@ -1026,3 +1057,9 @@ void MainWindow::showAndActivate() m_actionRestore->setVisible(false); m_actionMinimize->setVisible(true); } + +QString MainWindow::getTlsPath() +{ + CoreTool coreTool; + return QStringLiteral("%1/%2").arg(coreTool.getProfileDir(), kSslDir); +} diff --git a/src/apps/deskflow-gui/MainWindow.h b/src/apps/deskflow-gui/MainWindow.h index 239ca9a95..d6f07d0ab 100644 --- a/src/apps/deskflow-gui/MainWindow.h +++ b/src/apps/deskflow-gui/MainWindow.h @@ -164,6 +164,8 @@ private: void updateStatus(); void showAndActivate(); + QString getTlsPath(); + VersionChecker m_versionChecker; bool m_secureSocket = false; deskflow::gui::config::ServerConfigDialogState m_serverConfigDialogState; diff --git a/src/lib/common/constants.h.in b/src/lib/common/constants.h.in index e5a7db4ce..266362128 100644 --- a/src/lib/common/constants.h.in +++ b/src/lib/common/constants.h.in @@ -29,4 +29,5 @@ const auto kDebugBuild = false; const auto kSslDir = "tls"; const auto kCertificateFilename = "@CMAKE_PROJECT_NAME@.pem"; +const auto kFingerprintLocalFilename = "local-fingerprint"; const auto kFingerprintTrustedServersFilename = "trusted-servers"; diff --git a/src/lib/gui/CMakeLists.txt b/src/lib/gui/CMakeLists.txt index 2c9435a1d..210d78e80 100644 --- a/src/lib/gui/CMakeLists.txt +++ b/src/lib/gui/CMakeLists.txt @@ -77,8 +77,6 @@ add_library(${target} STATIC proxy/QSettingsProxy.h tls/TlsCertificate.cpp tls/TlsCertificate.h - tls/TlsFingerprint.cpp - tls/TlsFingerprint.h tls/TlsUtility.cpp tls/TlsUtility.h validators/AliasValidator.cpp diff --git a/src/lib/gui/tls/TlsCertificate.cpp b/src/lib/gui/tls/TlsCertificate.cpp index 0c61f40e1..1f9e3e323 100644 --- a/src/lib/gui/tls/TlsCertificate.cpp +++ b/src/lib/gui/tls/TlsCertificate.cpp @@ -7,9 +7,10 @@ #include "TlsCertificate.h" -#include "TlsFingerprint.h" - #include "common/constants.h" +#include "gui/core/CoreTool.h" +#include "net/FingerprintData.h" +#include "net/FingerprintDatabase.h" #include "net/SecureUtils.h" #include @@ -47,7 +48,18 @@ bool TlsCertificate::generateFingerprint(const QString &certificateFilename) try { auto fingerprint = deskflow::pemFileCertFingerprint(certificateFilename.toStdString(), deskflow::FingerprintType::SHA1); - TlsFingerprint::local().trust(QString::fromStdString(deskflow::formatSSLFingerprint(fingerprint)), false); + + CoreTool coreTool; + QString profileDir = coreTool.getProfileDir(); + + auto localPath = QStringLiteral("%1/%2/%3").arg(profileDir, kSslDir, kFingerprintLocalFilename).toStdString(); + + const deskflow::FingerprintData data{"sha1", fingerprint}; + + deskflow::FingerprintDatabase db; + db.addTrusted(data); + db.write(localPath); + qDebug("tls fingerprint generated"); return true; } catch (const std::exception &e) { diff --git a/src/lib/gui/tls/TlsFingerprint.cpp b/src/lib/gui/tls/TlsFingerprint.cpp deleted file mode 100644 index bed144609..000000000 --- a/src/lib/gui/tls/TlsFingerprint.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * SPDX-FileCopyrightText: (C) 2015 Symless Ltd. - * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception - */ - -#include "TlsFingerprint.h" - -#include "gui/core/CoreTool.h" - -#include -#include - -// TODO: Reduce duplication of these strings between here and SecureSocket.cpp -static const char kDirName[] = "tls"; -static const char kLocalFilename[] = "local-fingerprint"; -static const char kTrustedServersFilename[] = "trusted-servers"; -static const char kTrustedClientsFilename[] = "trusted-clients"; - -TlsFingerprint::TlsFingerprint(const QString &filename) : m_Filename(filename) -{ -} - -void TlsFingerprint::trust(const QString &fingerprintText, bool append) const -{ - TlsFingerprint::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 TlsFingerprint::fileExists() const -{ - QString dirName = TlsFingerprint::directoryPath(); - if (!QDir(dirName).exists()) { - return false; - } - - QFile file(filePath()); - return file.exists(); -} - -bool TlsFingerprint::isTrusted(const QString &fingerprintText) const -{ - const QStringList list = readList(); - for (const auto &trusted : list) { - if (trusted == fingerprintText) { - return true; - } - } - return false; -} - -QStringList TlsFingerprint::readList(const int readTo) const -{ - QStringList list; - - QString dirName = TlsFingerprint::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 TlsFingerprint::readFirst() const -{ - QStringList list = readList(1); - return list.at(0); -} - -QString TlsFingerprint::filePath() const -{ - QString dir = TlsFingerprint::directoryPath(); - return QString("%1/%2").arg(dir, m_Filename); -} - -void TlsFingerprint::persistDirectory() -{ - QDir dir(TlsFingerprint::directoryPath()); - if (!dir.exists()) { - dir.mkpath("."); - } -} - -QString TlsFingerprint::directoryPath() -{ - CoreTool coreTool; - QString profileDir = coreTool.getProfileDir(); - - return QString("%1/%2").arg(profileDir, kDirName); -} - -TlsFingerprint TlsFingerprint::local() -{ - return TlsFingerprint(kLocalFilename); -} - -TlsFingerprint TlsFingerprint::trustedServers() -{ - return TlsFingerprint(kTrustedServersFilename); -} - -TlsFingerprint TlsFingerprint::trustedClients() -{ - return TlsFingerprint(kTrustedClientsFilename); -} diff --git a/src/lib/gui/tls/TlsFingerprint.h b/src/lib/gui/tls/TlsFingerprint.h deleted file mode 100644 index e196a5cc4..000000000 --- a/src/lib/gui/tls/TlsFingerprint.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * SPDX-FileCopyrightText: (C) 2015 Symless Ltd. - * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception - */ - -#pragma once - -#include - -class TlsFingerprint -{ -private: - explicit TlsFingerprint(const QString &filename); - -public: - static TlsFingerprint local(); - static TlsFingerprint trustedServers(); - static TlsFingerprint trustedClients(); - static QString directoryPath(); - static QString localFingerprint(); - static bool localFingerprintExists(); - static void persistDirectory(); - - void trust(const QString &fingerprintText, bool append = true) const; - bool isTrusted(const QString &fingerprintText) const; - QStringList readList(const int readTo = -1) const; - QString readFirst() const; - QString filePath() const; - bool fileExists() const; - -private: - QString m_Filename; -}; diff --git a/src/lib/net/SecureSocket.cpp b/src/lib/net/SecureSocket.cpp index 94234bd28..7ba987869 100644 --- a/src/lib/net/SecureSocket.cpp +++ b/src/lib/net/SecureSocket.cpp @@ -15,6 +15,7 @@ #include "base/TMethodEventJob.h" #include "common/constants.h" #include "mt/Lock.h" +#include "net/FingerprintDatabase.h" #include "net/TCPSocket.h" #include "net/TSocketMultiplexerMethodJob.h" #include @@ -621,34 +622,35 @@ bool SecureSocket::verifyCertFingerprint() return false; } - auto fingerprint = deskflow::formatSSLFingerprint(fingerprint_raw); - LOG((CLOG_NOTE "server fingerprint: %s", fingerprint.c_str())); + LOG((CLOG_NOTE "server fingerprint: %s", deskflow::formatSSLFingerprint(fingerprint_raw).c_str())); - std::string trustedServersFilename; - trustedServersFilename = deskflow::string::sprintf( + std::string trustedServersFilename = deskflow::string::sprintf( "%s/%s/%s", ARCH->getProfileDirectory().c_str(), kSslDir, kFingerprintTrustedServersFilename ); // check if this fingerprint exist std::string fileLine; std::ifstream file; - file.open(deskflow::filesystem::path(trustedServersFilename)); - bool isValid = false; - if (file.is_open()) { - while (!file.eof()) { - getline(file, fileLine); - if (!fileLine.empty() && !fileLine.compare(fingerprint)) { - isValid = true; - break; - } - } + deskflow::openUtf8Path(file, trustedServersFilename); + deskflow::FingerprintDatabase db; + db.read(trustedServersFilename); + if (!db.fingerprints().empty()) { + LOG((CLOG_NOTE "read %d fingerprints from %s", db.fingerprints().size(), trustedServersFilename.c_str())); } else { - LOG((CLOG_ERR "fail to open trusted fingerprints file: %s", trustedServersFilename.c_str())); + LOG((CLOG_ERR "failed to open trusted fingerprints file: %s", trustedServersFilename.c_str())); + return false; } - file.close(); - return isValid; + deskflow::FingerprintData fingerprint{"sha1", fingerprint_raw}; + + if (!db.isTrusted(fingerprint)) { + LOG((CLOG_WARN "fingerprint does not match trusted fingerprint")); + return false; + } + + LOG((CLOG_NOTE "fingerprint matches trusted fingerprint")); + return true; } ISocketMultiplexerJob *SecureSocket::serviceConnect(ISocketMultiplexerJob *job, bool, bool write, bool error)