From 0e406d491823bfc9dfed0fcc7934cdece8db7dd0 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:29 +0200 Subject: [PATCH 01/24] lib/net: Extract fingerprint formatting out of SecureSocket --- src/lib/net/SecureSocket.cpp | 22 ++------------------- src/lib/net/SecureSocket.h | 1 - src/lib/net/SecureUtils.cpp | 38 ++++++++++++++++++++++++++++++++++++ src/lib/net/SecureUtils.h | 25 ++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 21 deletions(-) create mode 100644 src/lib/net/SecureUtils.cpp create mode 100644 src/lib/net/SecureUtils.h diff --git a/src/lib/net/SecureSocket.cpp b/src/lib/net/SecureSocket.cpp index c3c1a064..f6ed0194 100644 --- a/src/lib/net/SecureSocket.cpp +++ b/src/lib/net/SecureSocket.cpp @@ -16,6 +16,7 @@ */ #include "SecureSocket.h" +#include "SecureUtils.h" #include "net/TSocketMultiplexerMethodJob.h" #include "base/TMethodEventJob.h" @@ -656,25 +657,6 @@ SecureSocket::disconnect() sendEvent(getEvents()->forIStream().inputShutdown()); } -void SecureSocket::formatFingerprint(std::string& fingerprint, bool hex, bool separator) -{ - if (hex) { - // to hexadecimal - barrier::string::toHex(fingerprint, 2); - } - - // all uppercase - barrier::string::uppercase(fingerprint); - - if (separator) { - // add colon to separate each 2 characters - size_t separators = fingerprint.size() / 2; - for (size_t i = 1; i < separators; i++) { - fingerprint.insert(i * 3 - 1, ":"); - } - } -} - bool SecureSocket::verifyCertFingerprint() { @@ -693,7 +675,7 @@ SecureSocket::verifyCertFingerprint() // format fingerprint into hexdecimal format with colon separator std::string fingerprint(reinterpret_cast(tempFingerprint), tempFingerprintLen); - formatFingerprint(fingerprint); + format_ssl_fingerprint(fingerprint); LOG((CLOG_NOTE "server fingerprint: %s", fingerprint.c_str())); std::string trustedServersFilename; diff --git a/src/lib/net/SecureSocket.h b/src/lib/net/SecureSocket.h index f861d662..24653b6f 100644 --- a/src/lib/net/SecureSocket.h +++ b/src/lib/net/SecureSocket.h @@ -68,7 +68,6 @@ private: void showError(const std::string& reason); std::string getError(); void disconnect(); - void formatFingerprint(std::string& fingerprint, bool hex = true, bool separator = true); bool verifyCertFingerprint(); MultiplexerJobStatus serviceConnect(ISocketMultiplexerJob*, bool, bool, bool); diff --git a/src/lib/net/SecureUtils.cpp b/src/lib/net/SecureUtils.cpp new file mode 100644 index 00000000..c796e9c7 --- /dev/null +++ b/src/lib/net/SecureUtils.cpp @@ -0,0 +1,38 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + 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 LICENSE 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 . +*/ + +#include "SecureUtils.h" +#include "base/String.h" + +void format_ssl_fingerprint(std::string& fingerprint, bool hex, bool separator) +{ + if (hex) { + // to hexadecimal + barrier::string::toHex(fingerprint, 2); + } + + // all uppercase + barrier::string::uppercase(fingerprint); + + if (separator) { + // add colon to separate each 2 characters + size_t separators = fingerprint.size() / 2; + for (size_t i = 1; i < separators; i++) { + fingerprint.insert(i * 3 - 1, ":"); + } + } +} diff --git a/src/lib/net/SecureUtils.h b/src/lib/net/SecureUtils.h new file mode 100644 index 00000000..7b6d09bc --- /dev/null +++ b/src/lib/net/SecureUtils.h @@ -0,0 +1,25 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + 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 LICENSE 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 . +*/ + +#ifndef BARRIER_LIB_NET_SECUREUTILS_H +#define BARRIER_LIB_NET_SECUREUTILS_H + +#include + +void format_ssl_fingerprint(std::string& fingerprint, bool hex = true, bool separator = true); + +#endif // BARRIER_LIB_NET_SECUREUTILS_H From 7f71924a862f64b984688b0c6eb3c14c64f60e2c Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:30 +0200 Subject: [PATCH 02/24] lib/net: Make format_ssl_fingerprint() easier to use --- src/lib/net/SecureSocket.cpp | 2 +- src/lib/net/SecureUtils.cpp | 11 ++++++----- src/lib/net/SecureUtils.h | 3 ++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/lib/net/SecureSocket.cpp b/src/lib/net/SecureSocket.cpp index f6ed0194..3d5e2be0 100644 --- a/src/lib/net/SecureSocket.cpp +++ b/src/lib/net/SecureSocket.cpp @@ -675,7 +675,7 @@ SecureSocket::verifyCertFingerprint() // format fingerprint into hexdecimal format with colon separator std::string fingerprint(reinterpret_cast(tempFingerprint), tempFingerprintLen); - format_ssl_fingerprint(fingerprint); + fingerprint = format_ssl_fingerprint(fingerprint); LOG((CLOG_NOTE "server fingerprint: %s", fingerprint.c_str())); std::string trustedServersFilename; diff --git a/src/lib/net/SecureUtils.cpp b/src/lib/net/SecureUtils.cpp index c796e9c7..72d4fbf0 100644 --- a/src/lib/net/SecureUtils.cpp +++ b/src/lib/net/SecureUtils.cpp @@ -18,21 +18,22 @@ #include "SecureUtils.h" #include "base/String.h" -void format_ssl_fingerprint(std::string& fingerprint, bool hex, bool separator) +std::string format_ssl_fingerprint(const std::string& fingerprint, bool hex, bool separator) { + std::string result = fingerprint; if (hex) { // to hexadecimal - barrier::string::toHex(fingerprint, 2); + barrier::string::toHex(result, 2); } // all uppercase - barrier::string::uppercase(fingerprint); + barrier::string::uppercase(result); if (separator) { // add colon to separate each 2 characters - size_t separators = fingerprint.size() / 2; + size_t separators = result.size() / 2; for (size_t i = 1; i < separators; i++) { - fingerprint.insert(i * 3 - 1, ":"); + result.insert(i * 3 - 1, ":"); } } } diff --git a/src/lib/net/SecureUtils.h b/src/lib/net/SecureUtils.h index 7b6d09bc..7c94fe92 100644 --- a/src/lib/net/SecureUtils.h +++ b/src/lib/net/SecureUtils.h @@ -20,6 +20,7 @@ #include -void format_ssl_fingerprint(std::string& fingerprint, bool hex = true, bool separator = true); +std::string format_ssl_fingerprint(const std::string& fingerprint, + bool hex = true, bool separator = true); #endif // BARRIER_LIB_NET_SECUREUTILS_H From 96e002157213bd127ab4a9c3ea4cab8e97cbbd5a Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:31 +0200 Subject: [PATCH 03/24] lib/base: Make to_hex() easier to use --- src/lib/base/String.cpp | 5 ++--- src/lib/base/String.h | 2 +- src/lib/net/SecureUtils.cpp | 3 ++- src/test/unittests/base/StringTests.cpp | 4 +--- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/lib/base/String.cpp b/src/lib/base/String.cpp index 389ca8aa..dcf7318a 100644 --- a/src/lib/base/String.cpp +++ b/src/lib/base/String.cpp @@ -185,8 +185,7 @@ removeFileExt(std::string filename) return filename.substr(0, dot); } -void -toHex(std::string& subject, int width, const char fill) +std::string to_hex(const std::string& subject, int width, const char fill) { std::stringstream ss; ss << std::hex; @@ -194,7 +193,7 @@ toHex(std::string& subject, int width, const char fill) ss << std::setw(width) << std::setfill(fill) << (int)(unsigned char)subject[i]; } - subject = ss.str(); + return ss.str(); } void diff --git a/src/lib/base/String.h b/src/lib/base/String.h index 047b6e16..a543cf86 100644 --- a/src/lib/base/String.h +++ b/src/lib/base/String.h @@ -75,7 +75,7 @@ std::string removeFileExt(std::string filename); /*! Convert each character in \c subject into hexdecimal form with \c width */ -void toHex(std::string& subject, int width, const char fill = '0'); +std::string to_hex(const std::string& subject, int width, const char fill = '0'); //! Convert to all uppercase /*! diff --git a/src/lib/net/SecureUtils.cpp b/src/lib/net/SecureUtils.cpp index 72d4fbf0..7c4a52da 100644 --- a/src/lib/net/SecureUtils.cpp +++ b/src/lib/net/SecureUtils.cpp @@ -23,7 +23,7 @@ std::string format_ssl_fingerprint(const std::string& fingerprint, bool hex, boo std::string result = fingerprint; if (hex) { // to hexadecimal - barrier::string::toHex(result, 2); + result = barrier::string::to_hex(result, 2); } // all uppercase @@ -36,4 +36,5 @@ std::string format_ssl_fingerprint(const std::string& fingerprint, bool hex, boo result.insert(i * 3 - 1, ":"); } } + return result; } diff --git a/src/test/unittests/base/StringTests.cpp b/src/test/unittests/base/StringTests.cpp index 5643aa53..fd771f42 100644 --- a/src/test/unittests/base/StringTests.cpp +++ b/src/test/unittests/base/StringTests.cpp @@ -59,9 +59,7 @@ TEST(StringTests, toHex_plaintext_hexString) String subject = "foobar"; int width = 2; - string::toHex(subject, width); - - EXPECT_EQ("666f6f626172", subject); + EXPECT_EQ("666f6f626172", string::to_hex(subject, width)); } TEST(StringTests, uppercase_lowercaseInput_uppercaseOutput) From 9d8e1faf59bbbc0360adc52b964d71a510f4e8a2 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:32 +0200 Subject: [PATCH 04/24] test: Add test for format_ssl_fingerprint() --- src/test/unittests/net/SecureUtilsTests.cpp | 47 +++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/test/unittests/net/SecureUtilsTests.cpp diff --git a/src/test/unittests/net/SecureUtilsTests.cpp b/src/test/unittests/net/SecureUtilsTests.cpp new file mode 100644 index 00000000..a77f158f --- /dev/null +++ b/src/test/unittests/net/SecureUtilsTests.cpp @@ -0,0 +1,47 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) 2021 Barrier contributors + + 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 LICENSE 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 . + */ + +#include "net/SecureUtils.h" + +#include "test/global/gtest.h" +#include + +namespace { + +std::string generate_pseudo_random_bytes(std::size_t seed, std::size_t size) +{ + std::mt19937_64 engine{seed}; + std::uniform_int_distribution dist{0, 255}; + std::vector bytes; + + bytes.reserve(size); + for (std::size_t i = 0; i < size; ++i) { + bytes.push_back(dist(engine)); + } + + return std::string{bytes.data(), bytes.size()}; +} + +} // namespace + +TEST(SecureUtilsTest, FormatSslFingerprintHexWithSeparators) +{ + std::string fingerprint = generate_pseudo_random_bytes(0, 32); + ASSERT_EQ(format_ssl_fingerprint(fingerprint, true, true), + "28:FD:0A:98:8A:0E:A1:6C:D7:E8:6C:A7:EE:58:41:71:" + "CA:B2:8E:49:25:94:90:25:26:05:8D:AF:63:ED:2E:30"); +} From 767f3d37ec71b13ed72ea07c9b0b3ff476af3307 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:33 +0200 Subject: [PATCH 05/24] test: Extract common test utilities to separate file --- src/test/global/TestUtils.cpp | 37 +++++++++++++++++++++ src/test/global/TestUtils.h | 30 +++++++++++++++++ src/test/unittests/net/SecureUtilsTests.cpp | 22 +++--------- 3 files changed, 71 insertions(+), 18 deletions(-) create mode 100644 src/test/global/TestUtils.cpp create mode 100644 src/test/global/TestUtils.h diff --git a/src/test/global/TestUtils.cpp b/src/test/global/TestUtils.cpp new file mode 100644 index 00000000..2aa7ca18 --- /dev/null +++ b/src/test/global/TestUtils.cpp @@ -0,0 +1,37 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + 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 LICENSE 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 . +*/ + +#include "TestUtils.h" +#include + +namespace barrier { + +std::string generate_pseudo_random_bytes(std::size_t seed, std::size_t size) +{ + std::mt19937_64 engine{seed}; + std::uniform_int_distribution dist{0, 255}; + std::vector bytes; + + bytes.reserve(size); + for (std::size_t i = 0; i < size; ++i) { + bytes.push_back(dist(engine)); + } + + return std::string{bytes.data(), bytes.size()}; +} + +} // namespace barrier diff --git a/src/test/global/TestUtils.h b/src/test/global/TestUtils.h new file mode 100644 index 00000000..b27e2c6e --- /dev/null +++ b/src/test/global/TestUtils.h @@ -0,0 +1,30 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + 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 LICENSE 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 . +*/ + +#ifndef BARRIER_TEST_GLOBAL_TEST_UTILS_H +#define BARRIER_TEST_GLOBAL_TEST_UTILS_H + +#include +#include + +namespace barrier { + +std::string generate_pseudo_random_bytes(std::size_t seed, std::size_t size); + +} // namespace barrier + +#endif // BARRIER_TEST_GLOBAL_TEST_UTILS_H diff --git a/src/test/unittests/net/SecureUtilsTests.cpp b/src/test/unittests/net/SecureUtilsTests.cpp index a77f158f..eb5ae498 100644 --- a/src/test/unittests/net/SecureUtilsTests.cpp +++ b/src/test/unittests/net/SecureUtilsTests.cpp @@ -18,25 +18,9 @@ #include "net/SecureUtils.h" #include "test/global/gtest.h" -#include +#include "test/global/TestUtils.h" -namespace { - -std::string generate_pseudo_random_bytes(std::size_t seed, std::size_t size) -{ - std::mt19937_64 engine{seed}; - std::uniform_int_distribution dist{0, 255}; - std::vector bytes; - - bytes.reserve(size); - for (std::size_t i = 0; i < size; ++i) { - bytes.push_back(dist(engine)); - } - - return std::string{bytes.data(), bytes.size()}; -} - -} // namespace +namespace barrier { TEST(SecureUtilsTest, FormatSslFingerprintHexWithSeparators) { @@ -45,3 +29,5 @@ TEST(SecureUtilsTest, FormatSslFingerprintHexWithSeparators) "28:FD:0A:98:8A:0E:A1:6C:D7:E8:6C:A7:EE:58:41:71:" "CA:B2:8E:49:25:94:90:25:26:05:8D:AF:63:ED:2E:30"); } + +} // namespace barrier From a9b30951ce6762353e28e9068af5364778f1fcb2 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:34 +0200 Subject: [PATCH 06/24] lib: Add utility function to convert from hex to binary --- src/lib/base/String.cpp | 56 +++++++++++++++++++++++++ src/lib/base/String.h | 3 ++ src/test/unittests/base/StringTests.cpp | 14 +++++++ 3 files changed, 73 insertions(+) diff --git a/src/lib/base/String.cpp b/src/lib/base/String.cpp index dcf7318a..416b1ec9 100644 --- a/src/lib/base/String.cpp +++ b/src/lib/base/String.cpp @@ -35,6 +35,42 @@ namespace barrier { namespace string { +namespace { + +// returns negative in case of non-matching character +int hex_to_number(char ch) +{ + switch (ch) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + + case 'a': return 10; + case 'b': return 11; + case 'c': return 12; + case 'd': return 13; + case 'e': return 14; + case 'f': return 15; + + case 'A': return 10; + case 'B': return 11; + case 'C': return 12; + case 'D': return 13; + case 'E': return 14; + case 'F': return 15; + } + return -1; +} + +} // namespace + std::string format(const char* fmt, ...) { @@ -196,6 +232,26 @@ std::string to_hex(const std::string& subject, int width, const char fill) return ss.str(); } +std::vector from_hex(const std::string& data) +{ + if ((data.size() % 2) != 0) { + return {}; + } + + std::vector result; + result.reserve(data.size() / 2); + + for (std::size_t i = 0; i < data.size(); i += 2) { + auto high = hex_to_number(data[i]); + auto low = hex_to_number(data[i + 1]); + if (high < 0 || low < 0) { + return {}; + } + result.push_back(high * 16 + low); + } + return result; +} + void uppercase(std::string& subject) { diff --git a/src/lib/base/String.h b/src/lib/base/String.h index a543cf86..4a2e43bf 100644 --- a/src/lib/base/String.h +++ b/src/lib/base/String.h @@ -77,6 +77,9 @@ Convert each character in \c subject into hexdecimal form with \c width */ std::string to_hex(const std::string& subject, int width, const char fill = '0'); +/// Convert binary data from hexadecimal +std::vector from_hex(const std::string& data); + //! Convert to all uppercase /*! Convert each character in \c subject to uppercase diff --git a/src/test/unittests/base/StringTests.cpp b/src/test/unittests/base/StringTests.cpp index fd771f42..c9c4732b 100644 --- a/src/test/unittests/base/StringTests.cpp +++ b/src/test/unittests/base/StringTests.cpp @@ -62,6 +62,20 @@ TEST(StringTests, toHex_plaintext_hexString) EXPECT_EQ("666f6f626172", string::to_hex(subject, width)); } +TEST(StringTests, fromhex_plaintext_string) +{ + auto result = string::from_hex("666f6f626172"); + std::string expected = "foobar"; + EXPECT_EQ(result, std::vector(expected.begin(), expected.end())); +} + +TEST(StringTests, fromhex_binary_string) +{ + auto result = string::from_hex("01020304050600fff9"); + auto expected = std::vector{1, 2, 3, 4, 5, 6, 0, 0xff, 0xf9}; + EXPECT_EQ(result, expected); +} + TEST(StringTests, uppercase_lowercaseInput_uppercaseOutput) { String subject = "12foo3BaR"; From b793675ef8cbf7f69fe8ba7cbdf3689e7f60c657 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:35 +0200 Subject: [PATCH 07/24] lib/net: Put secure utils into barrier namespace --- src/lib/net/SecureSocket.cpp | 2 +- src/lib/net/SecureUtils.cpp | 4 ++++ src/lib/net/SecureUtils.h | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/lib/net/SecureSocket.cpp b/src/lib/net/SecureSocket.cpp index 3d5e2be0..7186f249 100644 --- a/src/lib/net/SecureSocket.cpp +++ b/src/lib/net/SecureSocket.cpp @@ -675,7 +675,7 @@ SecureSocket::verifyCertFingerprint() // format fingerprint into hexdecimal format with colon separator std::string fingerprint(reinterpret_cast(tempFingerprint), tempFingerprintLen); - fingerprint = format_ssl_fingerprint(fingerprint); + fingerprint = barrier::format_ssl_fingerprint(fingerprint); LOG((CLOG_NOTE "server fingerprint: %s", fingerprint.c_str())); std::string trustedServersFilename; diff --git a/src/lib/net/SecureUtils.cpp b/src/lib/net/SecureUtils.cpp index 7c4a52da..d45d694f 100644 --- a/src/lib/net/SecureUtils.cpp +++ b/src/lib/net/SecureUtils.cpp @@ -18,6 +18,8 @@ #include "SecureUtils.h" #include "base/String.h" +namespace barrier { + std::string format_ssl_fingerprint(const std::string& fingerprint, bool hex, bool separator) { std::string result = fingerprint; @@ -38,3 +40,5 @@ std::string format_ssl_fingerprint(const std::string& fingerprint, bool hex, boo } return result; } + +} // namespace barrier diff --git a/src/lib/net/SecureUtils.h b/src/lib/net/SecureUtils.h index 7c94fe92..7dd680ed 100644 --- a/src/lib/net/SecureUtils.h +++ b/src/lib/net/SecureUtils.h @@ -20,7 +20,11 @@ #include +namespace barrier { + std::string format_ssl_fingerprint(const std::string& fingerprint, bool hex = true, bool separator = true); +} // namespace barrier + #endif // BARRIER_LIB_NET_SECUREUTILS_H From ef08470286fc95be69a8f6307797fc7502064235 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:36 +0200 Subject: [PATCH 08/24] src/lib: Use standard std::vsnprintf() instead of hacking our own --- CMakeLists.txt | 1 - res/config.h.in | 3 -- src/lib/arch/IArchString.h | 10 ---- src/lib/arch/unix/ArchStringUnix.cpp | 1 - src/lib/arch/vsnprintf.h | 67 ------------------------ src/lib/arch/win32/ArchStringWindows.cpp | 5 -- src/lib/base/Log.cpp | 2 +- src/lib/base/String.cpp | 2 +- 8 files changed, 2 insertions(+), 89 deletions(-) delete mode 100644 src/lib/arch/vsnprintf.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f4d7edac..1588ad7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,7 +91,6 @@ if (UNIX) check_function_exists (poll HAVE_POLL) check_function_exists (sigwait HAVE_POSIX_SIGWAIT) check_function_exists (strftime HAVE_STRFTIME) - check_function_exists (vsnprintf HAVE_VSNPRINTF) check_function_exists (inet_aton HAVE_INET_ATON) # For some reason, the check_function_exists macro doesn't detect diff --git a/res/config.h.in b/res/config.h.in index a2216875..53d3a3ec 100644 --- a/res/config.h.in +++ b/res/config.h.in @@ -94,9 +94,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNISTD_H ${HAVE_UNISTD_H} -/* Define to 1 if you have the `vsnprintf` function. */ -#cmakedefine HAVE_VSNPRINTF ${HAVE_VSNPRINTF} - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_WCHAR_H ${HAVE_WCHAR_H} diff --git a/src/lib/arch/IArchString.h b/src/lib/arch/IArchString.h index ad16fbea..f1803d8d 100644 --- a/src/lib/arch/IArchString.h +++ b/src/lib/arch/IArchString.h @@ -46,16 +46,6 @@ public: //! @name manipulators //@{ - //! printf() to limited size buffer with va_list - /*! - This method is equivalent to vsprintf() except it will not write - more than \c n bytes to the buffer, returning -1 if the output - was truncated and the number of bytes written not including the - trailing NUL otherwise. - */ - virtual int vsnprintf(char* str, - int size, const char* fmt, va_list ap); - //! Convert multibyte string to wide character string virtual int convStringMBToWC(wchar_t*, const char*, UInt32 n, bool* errors); diff --git a/src/lib/arch/unix/ArchStringUnix.cpp b/src/lib/arch/unix/ArchStringUnix.cpp index cddb8bd8..dbb91c1e 100644 --- a/src/lib/arch/unix/ArchStringUnix.cpp +++ b/src/lib/arch/unix/ArchStringUnix.cpp @@ -25,7 +25,6 @@ // #include "arch/multibyte.h" -#include "arch/vsnprintf.h" ArchStringUnix::ArchStringUnix() { diff --git a/src/lib/arch/vsnprintf.h b/src/lib/arch/vsnprintf.h deleted file mode 100644 index 5422f270..00000000 --- a/src/lib/arch/vsnprintf.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 2002 Chris Schoeneman - * - * 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 LICENSE 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 . - */ - -#include "arch/IArchString.h" - -#if HAVE_VSNPRINTF - -#if !defined(ARCH_VSNPRINTF) -# define ARCH_VSNPRINTF vsnprintf -#endif - -int -IArchString::vsnprintf(char* str, int size, const char* fmt, va_list ap) -{ - int n = ::ARCH_VSNPRINTF(str, size, fmt, ap); - if (n > size) { - n = -1; - } - return n; -} - -#elif SYSAPI_UNIX // !HAVE_VSNPRINTF - -#include - -int -IArchString::vsnprintf(char* str, int size, const char* fmt, va_list ap) -{ - static FILE* bitbucket = fopen("/dev/null", "w"); - if (bitbucket == NULL) { - // uh oh - if (size > 0) { - str[0] = '\0'; - } - return 0; - } - else { - // count the characters using the bitbucket - int n = vfprintf(bitbucket, fmt, ap); - if (n + 1 <= size) { - // it'll fit so print it into str - vsprintf(str, fmt, ap); - } - return n; - } -} - -#else // !HAVE_VSNPRINTF && !SYSAPI_UNIX - -#error vsnprintf not implemented - -#endif // !HAVE_VSNPRINTF diff --git a/src/lib/arch/win32/ArchStringWindows.cpp b/src/lib/arch/win32/ArchStringWindows.cpp index c570d1be..00336590 100644 --- a/src/lib/arch/win32/ArchStringWindows.cpp +++ b/src/lib/arch/win32/ArchStringWindows.cpp @@ -26,11 +26,6 @@ // ArchStringWindows // -#include "arch/multibyte.h" -#define HAVE_VSNPRINTF 1 -#define ARCH_VSNPRINTF _vsnprintf -#include "arch/vsnprintf.h" - ArchStringWindows::ArchStringWindows() { } diff --git a/src/lib/base/Log.cpp b/src/lib/base/Log.cpp index 66a5364b..8f52a805 100644 --- a/src/lib/base/Log.cpp +++ b/src/lib/base/Log.cpp @@ -145,7 +145,7 @@ Log::print(const char* file, int line, const char* fmt, ...) // try printing into the buffer va_list args; va_start(args, fmt); - int n = ARCH->vsnprintf(buffer, len - sPad, fmt, args); + int n = std::vsnprintf(buffer, len - sPad, fmt, args); va_end(args); // if the buffer wasn't big enough then make it bigger and try again diff --git a/src/lib/base/String.cpp b/src/lib/base/String.cpp index 416b1ec9..19ee8f62 100644 --- a/src/lib/base/String.cpp +++ b/src/lib/base/String.cpp @@ -171,7 +171,7 @@ sprintf(const char* fmt, ...) // try printing into the buffer va_list args; va_start(args, fmt); - int n = ARCH->vsnprintf(buffer, len, fmt, args); + int n = std::vsnprintf(buffer, len, fmt, args); va_end(args); // if the buffer wasn't big enough then make it bigger and try again From cd7e731cb730e912f512ea8b5821aa7b10f90b52 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:37 +0200 Subject: [PATCH 09/24] lib: Switch to std::vector for fingerprint data --- src/lib/base/String.cpp | 6 +++--- src/lib/base/String.h | 2 +- src/lib/net/SecureSocket.cpp | 6 ++++-- src/lib/net/SecureUtils.cpp | 8 ++------ src/lib/net/SecureUtils.h | 5 +++-- src/test/global/TestUtils.cpp | 6 +++--- src/test/global/TestUtils.h | 4 ++-- src/test/unittests/base/StringTests.cpp | 2 +- src/test/unittests/net/SecureUtilsTests.cpp | 4 ++-- 9 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/lib/base/String.cpp b/src/lib/base/String.cpp index 19ee8f62..413ad449 100644 --- a/src/lib/base/String.cpp +++ b/src/lib/base/String.cpp @@ -221,12 +221,12 @@ removeFileExt(std::string filename) return filename.substr(0, dot); } -std::string to_hex(const std::string& subject, int width, const char fill) +std::string to_hex(const std::vector& 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]; + for (unsigned int i = 0; i < subject.size(); i++) { + ss << std::setw(width) << std::setfill(fill) << static_cast(subject[i]); } return ss.str(); diff --git a/src/lib/base/String.h b/src/lib/base/String.h index 4a2e43bf..9c5a53ba 100644 --- a/src/lib/base/String.h +++ b/src/lib/base/String.h @@ -75,7 +75,7 @@ std::string removeFileExt(std::string filename); /*! Convert each character in \c subject into hexdecimal form with \c width */ -std::string to_hex(const std::string& subject, int width, const char fill = '0'); +std::string to_hex(const std::vector& subject, int width, const char fill = '0'); /// Convert binary data from hexadecimal std::vector from_hex(const std::string& data); diff --git a/src/lib/net/SecureSocket.cpp b/src/lib/net/SecureSocket.cpp index 7186f249..39f2a1fd 100644 --- a/src/lib/net/SecureSocket.cpp +++ b/src/lib/net/SecureSocket.cpp @@ -674,8 +674,10 @@ SecureSocket::verifyCertFingerprint() } // format fingerprint into hexdecimal format with colon separator - std::string fingerprint(reinterpret_cast(tempFingerprint), tempFingerprintLen); - fingerprint = barrier::format_ssl_fingerprint(fingerprint); + std::vector fingerprint_raw; + fingerprint_raw.assign(reinterpret_cast(tempFingerprint), + reinterpret_cast(tempFingerprint) + tempFingerprintLen); + auto fingerprint = barrier::format_ssl_fingerprint(fingerprint_raw); LOG((CLOG_NOTE "server fingerprint: %s", fingerprint.c_str())); std::string trustedServersFilename; diff --git a/src/lib/net/SecureUtils.cpp b/src/lib/net/SecureUtils.cpp index d45d694f..000c56ed 100644 --- a/src/lib/net/SecureUtils.cpp +++ b/src/lib/net/SecureUtils.cpp @@ -20,13 +20,9 @@ namespace barrier { -std::string format_ssl_fingerprint(const std::string& fingerprint, bool hex, bool separator) +std::string format_ssl_fingerprint(const std::vector& fingerprint, bool separator) { - std::string result = fingerprint; - if (hex) { - // to hexadecimal - result = barrier::string::to_hex(result, 2); - } + std::string result = barrier::string::to_hex(fingerprint, 2); // all uppercase barrier::string::uppercase(result); diff --git a/src/lib/net/SecureUtils.h b/src/lib/net/SecureUtils.h index 7dd680ed..50e944e1 100644 --- a/src/lib/net/SecureUtils.h +++ b/src/lib/net/SecureUtils.h @@ -19,11 +19,12 @@ #define BARRIER_LIB_NET_SECUREUTILS_H #include +#include namespace barrier { -std::string format_ssl_fingerprint(const std::string& fingerprint, - bool hex = true, bool separator = true); +std::string format_ssl_fingerprint(const std::vector& fingerprint, + bool separator = true); } // namespace barrier diff --git a/src/test/global/TestUtils.cpp b/src/test/global/TestUtils.cpp index 2aa7ca18..6a3193bf 100644 --- a/src/test/global/TestUtils.cpp +++ b/src/test/global/TestUtils.cpp @@ -20,18 +20,18 @@ namespace barrier { -std::string generate_pseudo_random_bytes(std::size_t seed, std::size_t size) +std::vector generate_pseudo_random_bytes(std::size_t seed, std::size_t size) { std::mt19937_64 engine{seed}; std::uniform_int_distribution dist{0, 255}; - std::vector bytes; + std::vector bytes; bytes.reserve(size); for (std::size_t i = 0; i < size; ++i) { bytes.push_back(dist(engine)); } - return std::string{bytes.data(), bytes.size()}; + return bytes; } } // namespace barrier diff --git a/src/test/global/TestUtils.h b/src/test/global/TestUtils.h index b27e2c6e..31050ece 100644 --- a/src/test/global/TestUtils.h +++ b/src/test/global/TestUtils.h @@ -19,11 +19,11 @@ #define BARRIER_TEST_GLOBAL_TEST_UTILS_H #include -#include +#include namespace barrier { -std::string generate_pseudo_random_bytes(std::size_t seed, std::size_t size); +std::vector generate_pseudo_random_bytes(std::size_t seed, std::size_t size); } // namespace barrier diff --git a/src/test/unittests/base/StringTests.cpp b/src/test/unittests/base/StringTests.cpp index c9c4732b..ad5d5157 100644 --- a/src/test/unittests/base/StringTests.cpp +++ b/src/test/unittests/base/StringTests.cpp @@ -56,7 +56,7 @@ TEST(StringTests, sprintf_formatWithArgument_formatedString) TEST(StringTests, toHex_plaintext_hexString) { - String subject = "foobar"; + std::vector subject{'f', 'o', 'o', 'b', 'a', 'r'}; int width = 2; EXPECT_EQ("666f6f626172", string::to_hex(subject, width)); diff --git a/src/test/unittests/net/SecureUtilsTests.cpp b/src/test/unittests/net/SecureUtilsTests.cpp index eb5ae498..c2394bf9 100644 --- a/src/test/unittests/net/SecureUtilsTests.cpp +++ b/src/test/unittests/net/SecureUtilsTests.cpp @@ -24,8 +24,8 @@ namespace barrier { TEST(SecureUtilsTest, FormatSslFingerprintHexWithSeparators) { - std::string fingerprint = generate_pseudo_random_bytes(0, 32); - ASSERT_EQ(format_ssl_fingerprint(fingerprint, true, true), + auto fingerprint = generate_pseudo_random_bytes(0, 32); + ASSERT_EQ(format_ssl_fingerprint(fingerprint, true), "28:FD:0A:98:8A:0E:A1:6C:D7:E8:6C:A7:EE:58:41:71:" "CA:B2:8E:49:25:94:90:25:26:05:8D:AF:63:ED:2E:30"); } From 85486927b342cd7eceb5d1fd8315a95d5f09f9fd Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:38 +0200 Subject: [PATCH 10/24] lib/base: Implement pattern to execute something at function exit --- src/lib/base/finally.h | 61 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/lib/base/finally.h diff --git a/src/lib/base/finally.h b/src/lib/base/finally.h new file mode 100644 index 00000000..f3be617c --- /dev/null +++ b/src/lib/base/finally.h @@ -0,0 +1,61 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + 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 LICENSE 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 . +*/ + +#ifndef BARRIER_LIB_BASE_FINALLY_H +#define BARRIER_LIB_BASE_FINALLY_H + +#include + +namespace barrier { + +// this implements a common pattern of executing an action at the end of function + +template +class final_action { +public: + final_action() noexcept {} + final_action(Callable callable) noexcept : callable_{callable} {} + + ~final_action() noexcept + { + if (!invoked_) { + callable_(); + } + } + + final_action(final_action&& other) noexcept : + callable_{std::move(other.callable_)} + { + std::swap(invoked_, other.invoked_); + } + + final_action(const final_action&) = delete; + final_action& operator=(const final_action&) = delete; +private: + bool invoked_ = false; + Callable callable_; +}; + +template +inline final_action finally(Callable&& callable) noexcept +{ + return final_action(std::forward(callable)); +} + +} // namespace barrier + +#endif // BARRIER_LIB_BASE_FINALLY_H From 089b8e474978d1e8df9b0a0fc9e172a862409401 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:39 +0200 Subject: [PATCH 11/24] lib/net: Extract SSL fingerprint generation to reusable function --- src/lib/net/SecureSocket.cpp | 19 ++++++------------ src/lib/net/SecureUtils.cpp | 39 ++++++++++++++++++++++++++++++++++++ src/lib/net/SecureUtils.h | 9 +++++++++ 3 files changed, 54 insertions(+), 13 deletions(-) diff --git a/src/lib/net/SecureSocket.cpp b/src/lib/net/SecureSocket.cpp index 39f2a1fd..286394c4 100644 --- a/src/lib/net/SecureSocket.cpp +++ b/src/lib/net/SecureSocket.cpp @@ -661,22 +661,15 @@ 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)); + std::vector fingerprint_raw; + try { + fingerprint_raw = barrier::get_ssl_cert_fingerprint(SSL_get_peer_certificate(m_ssl->m_ssl), + barrier::FingerprintType::SHA1); + } catch (const std::exception& e) { + LOG((CLOG_ERR "%s", e.what())); return false; } - // format fingerprint into hexdecimal format with colon separator - std::vector fingerprint_raw; - fingerprint_raw.assign(reinterpret_cast(tempFingerprint), - reinterpret_cast(tempFingerprint) + tempFingerprintLen); auto fingerprint = barrier::format_ssl_fingerprint(fingerprint_raw); LOG((CLOG_NOTE "server fingerprint: %s", fingerprint.c_str())); diff --git a/src/lib/net/SecureUtils.cpp b/src/lib/net/SecureUtils.cpp index 000c56ed..c9222432 100644 --- a/src/lib/net/SecureUtils.cpp +++ b/src/lib/net/SecureUtils.cpp @@ -18,8 +18,26 @@ #include "SecureUtils.h" #include "base/String.h" +#include +#include +#include +#include + namespace barrier { +namespace { + +const EVP_MD* get_digest_for_type(FingerprintType type) +{ + switch (type) { + case FingerprintType::SHA1: return EVP_sha1(); + case FingerprintType::SHA256: return EVP_sha256(); + } + throw std::runtime_error("Unknown fingerprint type " + std::to_string(static_cast(type))); +} + +} // namespace + std::string format_ssl_fingerprint(const std::vector& fingerprint, bool separator) { std::string result = barrier::string::to_hex(fingerprint, 2); @@ -37,4 +55,25 @@ std::string format_ssl_fingerprint(const std::vector& fingerprint, bool return result; } +std::vector get_ssl_cert_fingerprint(X509* cert, FingerprintType type) +{ + if (!cert) { + throw std::runtime_error("certificate is null"); + } + + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int digest_length = 0; + int result = X509_digest(cert, get_digest_for_type(type), digest, &digest_length); + + if (result <= 0) { + throw std::runtime_error("failed to calculate fingerprint, digest result: " + + std::to_string(result)); + } + + std::vector digest_vec; + digest_vec.assign(reinterpret_cast(digest), + reinterpret_cast(digest) + digest_length); + return digest_vec; +} + } // namespace barrier diff --git a/src/lib/net/SecureUtils.h b/src/lib/net/SecureUtils.h index 50e944e1..a35c1db7 100644 --- a/src/lib/net/SecureUtils.h +++ b/src/lib/net/SecureUtils.h @@ -18,14 +18,23 @@ #ifndef BARRIER_LIB_NET_SECUREUTILS_H #define BARRIER_LIB_NET_SECUREUTILS_H +#include +#include #include #include namespace barrier { +enum FingerprintType { + SHA1, // deprecated + SHA256, +}; + std::string format_ssl_fingerprint(const std::vector& fingerprint, bool separator = true); +std::vector get_ssl_cert_fingerprint(X509* cert, FingerprintType type); + } // namespace barrier #endif // BARRIER_LIB_NET_SECUREUTILS_H From cf732aba37422898c43e5b1ab50c0d1969390320 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:48 +0200 Subject: [PATCH 12/24] lib/io: Add a replacement for fopen() which works on Windows fopen() does not correctly handle non-ASCII paths on Windows. --- src/lib/io/fstream.cpp | 12 ++++++++++++ src/lib/io/fstream.h | 3 +++ 2 files changed, 15 insertions(+) diff --git a/src/lib/io/fstream.cpp b/src/lib/io/fstream.cpp index 4aef9073..ea91859d 100644 --- a/src/lib/io/fstream.cpp +++ b/src/lib/io/fstream.cpp @@ -54,4 +54,16 @@ void open_utf8_path(std::fstream& stream, const std::string& path, std::ios_base open_utf8_path_impl(stream, path, mode); } +std::FILE* fopen_utf8_path(const std::string& path, const std::string& mode) +{ +#if SYSAPI_WIN32 + auto wchar_path = utf8_to_win_char(path); + auto wchar_mode = utf8_to_win_char(mode); + return _wfopen(reinterpret_cast(wchar_path.data()), + reinterpret_cast(wchar_mode.data())); +#else + return std::fopen(path.c_str(), mode.c_str()); +#endif +} + } // namespace barrier diff --git a/src/lib/io/fstream.h b/src/lib/io/fstream.h index 26288373..2b327f18 100644 --- a/src/lib/io/fstream.h +++ b/src/lib/io/fstream.h @@ -18,6 +18,7 @@ #ifndef BARRIER_LIB_IO_FSTREAM_H #define BARRIER_LIB_IO_FSTREAM_H +#include #include #include @@ -30,6 +31,8 @@ void open_utf8_path(std::ofstream& stream, const std::string& path, void open_utf8_path(std::fstream& stream, const std::string& path, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out); +std::FILE* fopen_utf8_path(const std::string& path, const std::string& mode); + } // namespace barrier #endif From dbf56a937544c341bbe0cec5160c8d45863c539a Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:40 +0200 Subject: [PATCH 13/24] gui: Use openssl library instead of CLI tool to generate fingerprints --- src/gui/CMakeLists.txt | 2 +- src/gui/src/SslCertificate.cpp | 35 ++++++++-------------------------- src/lib/net/SecureUtils.cpp | 21 ++++++++++++++++++++ src/lib/net/SecureUtils.h | 3 +++ 4 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 2875adf4..49557352 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -131,7 +131,7 @@ add_executable (barrier WIN32 include_directories (./src) -target_link_libraries (barrier Qt5::Core Qt5::Widgets Qt5::Network ${OPENSSL_LIBS}) +target_link_libraries(barrier net base io Qt5::Core Qt5::Widgets Qt5::Network ${OPENSSL_LIBS}) target_compile_definitions (barrier PRIVATE -DBARRIER_VERSION_STAGE="${BARRIER_VERSION_STAGE}") target_compile_definitions (barrier PRIVATE -DBARRIER_REVISION="${BARRIER_REVISION}") diff --git a/src/gui/src/SslCertificate.cpp b/src/gui/src/SslCertificate.cpp index 9dc93ce8..99af4d44 100644 --- a/src/gui/src/SslCertificate.cpp +++ b/src/gui/src/SslCertificate.cpp @@ -18,6 +18,7 @@ #include "SslCertificate.h" #include "Fingerprint.h" #include "common/DataDirectories.h" +#include "net/SecureUtils.h" #include #include @@ -149,34 +150,14 @@ void SslCertificate::generateCertificate() 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); - - auto ret = runTool(arguments); - bool success = ret.first; - std::string output = ret.second; - - if (!success) { - return; - } - - // find the fingerprint from the tool output - auto i = output.find_first_of('='); - if (i != std::string::npos) { - i++; - auto fingerprint = output.substr( - i, output.size() - i); - - Fingerprint::local().trust(QString::fromStdString(fingerprint), false); + try { + auto fingerprint = barrier::get_pem_file_cert_fingerprint(certificateFilename.toStdString(), + barrier::FingerprintType::SHA1); + Fingerprint::local().trust(QString::fromStdString( + barrier::format_ssl_fingerprint(fingerprint)), false); emit info(tr("SSL fingerprint generated.")); - } - else { - emit error(tr("Failed to find SSL fingerprint.")); + } catch (const std::exception& e) { + emit error(tr("Failed to find SSL fingerprint.") + e.what()); } } diff --git a/src/lib/net/SecureUtils.cpp b/src/lib/net/SecureUtils.cpp index c9222432..97c9cf9a 100644 --- a/src/lib/net/SecureUtils.cpp +++ b/src/lib/net/SecureUtils.cpp @@ -17,10 +17,13 @@ #include "SecureUtils.h" #include "base/String.h" +#include "base/finally.h" +#include "io/fstream.h" #include #include #include +#include #include namespace barrier { @@ -76,4 +79,22 @@ std::vector get_ssl_cert_fingerprint(X509* cert, FingerprintType t return digest_vec; } +std::vector get_pem_file_cert_fingerprint(const std::string& path, + FingerprintType type) +{ + auto fp = fopen_utf8_path(path, "r"); + if (!fp) { + throw std::runtime_error("Could not open certificate path"); + } + auto file_close = finally([fp]() { std::fclose(fp); }); + + X509* cert = PEM_read_X509(fp, nullptr, nullptr, nullptr); + if (!cert) { + throw std::runtime_error("Certificate could not be parsed"); + } + auto cert_free = finally([cert]() { X509_free(cert); }); + + return get_ssl_cert_fingerprint(cert, type); +} + } // namespace barrier diff --git a/src/lib/net/SecureUtils.h b/src/lib/net/SecureUtils.h index a35c1db7..6a6343c8 100644 --- a/src/lib/net/SecureUtils.h +++ b/src/lib/net/SecureUtils.h @@ -35,6 +35,9 @@ std::string format_ssl_fingerprint(const std::vector& fingerprint, std::vector get_ssl_cert_fingerprint(X509* cert, FingerprintType type); +std::vector get_pem_file_cert_fingerprint(const std::string& path, + FingerprintType type); + } // namespace barrier #endif // BARRIER_LIB_NET_SECUREUTILS_H From aa3afa9062f71253c80e34e00d06860939c9b432 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:41 +0200 Subject: [PATCH 14/24] Use openssl library instead of CLI to generate certificates --- doc/newsfragments/dont-use-openssl-cli.bugfix | 1 + res/openssl/barrier.conf | 65 -------------- src/gui/src/SslCertificate.cpp | 88 +------------------ src/lib/net/SecureUtils.cpp | 45 ++++++++++ src/lib/net/SecureUtils.h | 2 + 5 files changed, 52 insertions(+), 149 deletions(-) create mode 100644 doc/newsfragments/dont-use-openssl-cli.bugfix delete mode 100644 res/openssl/barrier.conf diff --git a/doc/newsfragments/dont-use-openssl-cli.bugfix b/doc/newsfragments/dont-use-openssl-cli.bugfix new file mode 100644 index 00000000..316d6abf --- /dev/null +++ b/doc/newsfragments/dont-use-openssl-cli.bugfix @@ -0,0 +1 @@ +Barrier no longer uses openssl CLI tool for any operations and hooks into the openssl library directly. diff --git a/res/openssl/barrier.conf b/res/openssl/barrier.conf deleted file mode 100644 index a29abfd5..00000000 --- a/res/openssl/barrier.conf +++ /dev/null @@ -1,65 +0,0 @@ -# -# Barrier 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 = 2048 # 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 diff --git a/src/gui/src/SslCertificate.cpp b/src/gui/src/SslCertificate.cpp index 99af4d44..ac70d01a 100644 --- a/src/gui/src/SslCertificate.cpp +++ b/src/gui/src/SslCertificate.cpp @@ -30,16 +30,8 @@ #include #include -static const char kCertificateLifetime[] = "365"; -static const char kCertificateSubjectInfo[] = "/CN=Barrier"; static const char kCertificateFilename[] = "Barrier.pem"; static const char kSslDir[] = "SSL"; -static const char kUnixOpenSslCommand[] = "openssl"; - -#if defined(Q_OS_WIN) -static const char kWinOpenSslBinary[] = "openssl.exe"; -static const char kConfigFile[] = "barrier.conf"; -#endif SslCertificate::SslCertificate(QObject *parent) : QObject(parent) @@ -50,93 +42,21 @@ SslCertificate::SslCertificate(QObject *parent) : } } -std::pair 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(); - std::string output; - - QString standardError; - if (success && process.waitForFinished()) - { - output = process.readAllStandardOutput().trimmed().toStdString(); - 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, output}; - } - - return {true, output}; -} - void SslCertificate::generateCertificate() { auto filename = QString::fromStdString(getCertificatePath()); QFile file(filename); if (!file.exists() || !isCertificateValid(filename)) { - 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(kCertificateSubjectInfo); - arguments.append(subInfo); - - // private key - arguments.append("-newkey"); - arguments.append("rsa:2048"); - QDir sslDir(QString::fromStdString(getCertificateDirectory())); if (!sslDir.exists()) { sslDir.mkpath("."); } - // key output filename - arguments.append("-keyout"); - arguments.append(filename); - - // certificate output filename - arguments.append("-out"); - arguments.append(filename); - - if (!runTool(arguments).first) { + try { + barrier::generate_pem_self_signed_cert(filename.toStdString()); + } catch (const std::exception& e) { + emit error(QString("SSL tool failed: %1").arg(e.what())); return; } diff --git a/src/lib/net/SecureUtils.cpp b/src/lib/net/SecureUtils.cpp index 97c9cf9a..4b081f66 100644 --- a/src/lib/net/SecureUtils.cpp +++ b/src/lib/net/SecureUtils.cpp @@ -20,6 +20,7 @@ #include "base/finally.h" #include "io/fstream.h" +#include #include #include #include @@ -97,4 +98,48 @@ std::vector get_pem_file_cert_fingerprint(const std::string& path, return get_ssl_cert_fingerprint(cert, type); } +void generate_pem_self_signed_cert(const std::string& path) +{ + auto expiration_days = 365; + + auto* private_key = EVP_PKEY_new(); + if (!private_key) { + throw std::runtime_error("Could not allocate private key for certificate"); + } + auto private_key_free = finally([private_key](){ EVP_PKEY_free(private_key); }); + + auto* rsa = RSA_generate_key(2048, RSA_F4, nullptr, nullptr); + if (!rsa) { + throw std::runtime_error("Failed to generate RSA key"); + } + EVP_PKEY_assign_RSA(private_key, rsa); + + auto* cert = X509_new(); + if (!cert) { + throw std::runtime_error("Could not allocate certificate"); + } + auto cert_free = 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), expiration_days * 24 * 3600); + X509_set_pubkey(cert, private_key); + + auto* name = X509_get_subject_name(cert); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, + reinterpret_cast("Barrier"), -1, -1, 0); + X509_set_issuer_name(cert, name); + + X509_sign(cert, private_key, EVP_sha256()); + + auto fp = fopen_utf8_path(path.c_str(), "r"); + if (!fp) { + throw std::runtime_error("Could not open certificate output path"); + } + auto file_close = finally([fp]() { std::fclose(fp); }); + + PEM_write_PrivateKey(fp, private_key, nullptr, nullptr, 0, nullptr, nullptr); + PEM_write_X509(fp, cert); +} + } // namespace barrier diff --git a/src/lib/net/SecureUtils.h b/src/lib/net/SecureUtils.h index 6a6343c8..df450136 100644 --- a/src/lib/net/SecureUtils.h +++ b/src/lib/net/SecureUtils.h @@ -38,6 +38,8 @@ std::vector get_ssl_cert_fingerprint(X509* cert, FingerprintType t std::vector get_pem_file_cert_fingerprint(const std::string& path, FingerprintType type); +void generate_pem_self_signed_cert(const std::string& path); + } // namespace barrier #endif // BARRIER_LIB_NET_SECUREUTILS_H From 8f88dc2585fcd03084c6296bbb5e73a540c94e23 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:42 +0200 Subject: [PATCH 15/24] lib/base: Support colons in from_hex() --- src/lib/base/String.cpp | 17 ++++++++++++----- src/test/unittests/base/StringTests.cpp | 14 ++++++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/lib/base/String.cpp b/src/lib/base/String.cpp index 413ad449..adbb11db 100644 --- a/src/lib/base/String.cpp +++ b/src/lib/base/String.cpp @@ -234,20 +234,27 @@ std::string to_hex(const std::vector& subject, int width, const ch std::vector from_hex(const std::string& data) { - if ((data.size() % 2) != 0) { - return {}; - } - std::vector result; result.reserve(data.size() / 2); - for (std::size_t i = 0; i < data.size(); i += 2) { + std::size_t i = 0; + while (i < data.size()) { + if (data[i] == ':') { + i++; + continue; + } + + if (i + 2 > data.size()) { + return {}; // uneven character count follows, it's unclear how to interpret it + } + auto high = hex_to_number(data[i]); auto low = hex_to_number(data[i + 1]); if (high < 0 || low < 0) { return {}; } result.push_back(high * 16 + low); + i += 2; } return result; } diff --git a/src/test/unittests/base/StringTests.cpp b/src/test/unittests/base/StringTests.cpp index ad5d5157..cc8e4fc6 100644 --- a/src/test/unittests/base/StringTests.cpp +++ b/src/test/unittests/base/StringTests.cpp @@ -69,6 +69,13 @@ TEST(StringTests, fromhex_plaintext_string) EXPECT_EQ(result, std::vector(expected.begin(), expected.end())); } +TEST(StringTests, fromhex_plaintext_string_colons) +{ + auto result = string::from_hex("66:6f:6f:62:61:72"); + std::string expected = "foobar"; + EXPECT_EQ(result, std::vector(expected.begin(), expected.end())); +} + TEST(StringTests, fromhex_binary_string) { auto result = string::from_hex("01020304050600fff9"); @@ -76,6 +83,13 @@ TEST(StringTests, fromhex_binary_string) EXPECT_EQ(result, expected); } +TEST(StringTests, fromhex_invalid_string) +{ + EXPECT_TRUE(string::from_hex("66:6").empty()); + EXPECT_TRUE(string::from_hex("66:612").empty()); + EXPECT_TRUE(string::from_hex("66:WW").empty()); +} + TEST(StringTests, uppercase_lowercaseInput_uppercaseOutput) { String subject = "12foo3BaR"; From 3e71b468f6dfd7b01752b551bddee3d9a8bc58dd Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:43 +0200 Subject: [PATCH 16/24] lib: Remove useless empty constructors --- src/lib/common/DataDirectories.h | 3 --- src/lib/common/PathUtilities.h | 4 ---- src/lib/platform/ImmuneKeysReader.h | 4 ---- 3 files changed, 11 deletions(-) diff --git a/src/lib/common/DataDirectories.h b/src/lib/common/DataDirectories.h index 6b990c20..783ff138 100644 --- a/src/lib/common/DataDirectories.h +++ b/src/lib/common/DataDirectories.h @@ -32,9 +32,6 @@ public: static const std::string& systemconfig(const std::string& path); private: - // static class - DataDirectories() {} - static std::string _profile; static std::string _global; static std::string _systemconfig; diff --git a/src/lib/common/PathUtilities.h b/src/lib/common/PathUtilities.h index 70b85b4c..30313243 100644 --- a/src/lib/common/PathUtilities.h +++ b/src/lib/common/PathUtilities.h @@ -24,8 +24,4 @@ class PathUtilities public: static std::string basename(const std::string& path); static std::string concat(const std::string& left, const std::string& right); - -private: - // static class - PathUtilities() {} }; diff --git a/src/lib/platform/ImmuneKeysReader.h b/src/lib/platform/ImmuneKeysReader.h index b46cbbe8..536dd45f 100644 --- a/src/lib/platform/ImmuneKeysReader.h +++ b/src/lib/platform/ImmuneKeysReader.h @@ -27,8 +27,4 @@ class ImmuneKeysReader { public: static bool get_list(const char * const path, std::vector &keys, std::string &badLine); - -private: - // static class - explicit ImmuneKeysReader() {} }; From 9cac96b4afa4e5f14145f2444af7d65852ff6254 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:44 +0200 Subject: [PATCH 17/24] lib/net: Implement a reusable fingerprint database --- src/lib/net/FingerprintDatabase.cpp | 140 ++++++++++++++++++ src/lib/net/FingerprintDatabase.h | 61 ++++++++ src/lib/net/FingerprintType.h | 54 +++++++ src/lib/net/SecureUtils.h | 6 +- .../net/FingerprintDatabaseTests.cpp | 95 ++++++++++++ 5 files changed, 351 insertions(+), 5 deletions(-) create mode 100644 src/lib/net/FingerprintDatabase.cpp create mode 100644 src/lib/net/FingerprintDatabase.h create mode 100644 src/lib/net/FingerprintType.h create mode 100644 src/test/unittests/net/FingerprintDatabaseTests.cpp diff --git a/src/lib/net/FingerprintDatabase.cpp b/src/lib/net/FingerprintDatabase.cpp new file mode 100644 index 00000000..cdc3ad32 --- /dev/null +++ b/src/lib/net/FingerprintDatabase.cpp @@ -0,0 +1,140 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + 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 LICENSE 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 . +*/ + +#include "base/String.h" +#include "FingerprintDatabase.h" +#include "io/fstream.h" +#include +#include + +namespace barrier { + +bool FingerprintData::operator==(const FingerprintData& other) const +{ + return algorithm == other.algorithm && data == other.data; +} + +void FingerprintDatabase::read(const std::string& path) +{ + std::ifstream file; + open_utf8_path(file, path, std::ios_base::in); + read_stream(file); +} + +void FingerprintDatabase::write(const std::string& path) +{ + std::ofstream file; + open_utf8_path(file, path, std::ios_base::out); + write_stream(file); +} + +void FingerprintDatabase::read_stream(std::istream& stream) +{ + if (!stream.good()) { + return; + } + + std::string line; + while (std::getline(stream, line)) { + if (line.empty()) { + continue; + } + + auto fingerprint = parse_db_line(line); + if (!fingerprint.valid()) { + continue; + } + + fingerprints_.push_back(fingerprint); + } +} + +void FingerprintDatabase::write_stream(std::ostream& stream) +{ + if (!stream.good()) { + return; + } + + for (const auto& fingerprint : fingerprints_) { + stream << to_db_line(fingerprint) << "\n"; + } +} + +void FingerprintDatabase::clear() +{ + fingerprints_.clear(); +} + +void FingerprintDatabase::add_trusted(const FingerprintData& fingerprint) +{ + if (is_trusted(fingerprint)) { + return; + } + fingerprints_.push_back(fingerprint); +} + +bool FingerprintDatabase::is_trusted(const FingerprintData& fingerprint) +{ + auto found_it = std::find(fingerprints_.begin(), fingerprints_.end(), fingerprint); + return found_it != fingerprints_.end(); +} + +FingerprintData FingerprintDatabase::parse_db_line(const std::string& line) +{ + FingerprintData result; + + // legacy v1 certificate handling + if (std::count(line.begin(), line.end(), ':') == 19 && line.size() == 40 + 19) { + auto data = string::from_hex(line); + if (data.empty()) { + return result; + } + result.algorithm = fingerprint_type_to_string(FingerprintType::SHA1); + result.data = data; + return result; + } + + auto version_end_pos = line.find(':'); + if (version_end_pos == std::string::npos) { + return result; + } + if (line.substr(0, version_end_pos) != "v2") { + return result; + } + auto algo_start_pos = version_end_pos + 1; + auto algo_end_pos = line.find(':', algo_start_pos); + if (algo_end_pos == std::string::npos) { + return result; + } + auto algorithm = line.substr(algo_start_pos, algo_end_pos - algo_start_pos); + auto data = string::from_hex(line.substr(algo_end_pos + 1)); + + if (data.empty()) { + return result; + } + + result.algorithm = algorithm; + result.data = data; + return result; +} + +std::string FingerprintDatabase::to_db_line(const FingerprintData& fingerprint) +{ + return "v2:" + fingerprint.algorithm + ":" + string::to_hex(fingerprint.data, 2); +} + +} // namespace barrier diff --git a/src/lib/net/FingerprintDatabase.h b/src/lib/net/FingerprintDatabase.h new file mode 100644 index 00000000..36ab39ce --- /dev/null +++ b/src/lib/net/FingerprintDatabase.h @@ -0,0 +1,61 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + 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 LICENSE 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 . +*/ + +#ifndef BARRIER_LIB_NET_FINGERPRINT_DATABASE_H +#define BARRIER_LIB_NET_FINGERPRINT_DATABASE_H + +#include "FingerprintType.h" +#include +#include +#include + +namespace barrier { + +struct FingerprintData { + std::string algorithm; + std::vector data; + + bool valid() const { return !algorithm.empty(); } + + bool operator==(const FingerprintData& other) const; +}; + +class FingerprintDatabase { +public: + void read(const std::string& path); + void write(const std::string& path); + + void read_stream(std::istream& stream); + void write_stream(std::ostream& stream); + + void clear(); + void add_trusted(const FingerprintData& fingerprint); + bool is_trusted(const FingerprintData& fingerprint); + + const std::vector& fingerprints() const { return fingerprints_; } + + static FingerprintData parse_db_line(const std::string& line); + static std::string to_db_line(const FingerprintData& fingerprint); + +private: + + std::vector fingerprints_; +}; + +} // namespace barrier + +#endif // BARRIER_LIB_NET_FINGERPRINT_DATABASE_H diff --git a/src/lib/net/FingerprintType.h b/src/lib/net/FingerprintType.h new file mode 100644 index 00000000..4e58e9f6 --- /dev/null +++ b/src/lib/net/FingerprintType.h @@ -0,0 +1,54 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + 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 LICENSE 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 . +*/ + +#ifndef BARRIER_LIB_NET_FINGERPRINT_TYPE_H +#define BARRIER_LIB_NET_FINGERPRINT_TYPE_H + +#include + +namespace barrier { + +enum FingerprintType { + INVALID, + SHA1, // deprecated + SHA256, +}; + +inline const char* fingerprint_type_to_string(FingerprintType type) +{ + switch (type) { + case FingerprintType::INVALID: return "invalid"; + case FingerprintType::SHA1: return "sha1"; + case FingerprintType::SHA256: return "sha256"; + } + return "invalid"; +} + +inline FingerprintType fingerprint_type_from_string(const std::string& type) +{ + if (type == "sha1") { + return FingerprintType::SHA1; + } + if (type == "sha256") { + return FingerprintType::SHA256; + } + return FingerprintType::INVALID; +} + +} // namespace barrier + +#endif // BARRIER_LIB_NET_FINGERPRINT_TYPE_H diff --git a/src/lib/net/SecureUtils.h b/src/lib/net/SecureUtils.h index df450136..ace0d45a 100644 --- a/src/lib/net/SecureUtils.h +++ b/src/lib/net/SecureUtils.h @@ -18,6 +18,7 @@ #ifndef BARRIER_LIB_NET_SECUREUTILS_H #define BARRIER_LIB_NET_SECUREUTILS_H +#include "FingerprintType.h" #include #include #include @@ -25,11 +26,6 @@ namespace barrier { -enum FingerprintType { - SHA1, // deprecated - SHA256, -}; - std::string format_ssl_fingerprint(const std::vector& fingerprint, bool separator = true); diff --git a/src/test/unittests/net/FingerprintDatabaseTests.cpp b/src/test/unittests/net/FingerprintDatabaseTests.cpp new file mode 100644 index 00000000..61bed0ea --- /dev/null +++ b/src/test/unittests/net/FingerprintDatabaseTests.cpp @@ -0,0 +1,95 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + 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 LICENSE 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 . +*/ + +#include "net/FingerprintDatabase.h" +#include "test/global/gtest.h" + +namespace barrier { + +TEST(FingerprintDatabase, parse_db_line) +{ + ASSERT_FALSE(FingerprintDatabase::parse_db_line("").valid()); + ASSERT_FALSE(FingerprintDatabase::parse_db_line("abcd").valid()); + ASSERT_FALSE(FingerprintDatabase::parse_db_line("v1:algo:something").valid()); + ASSERT_FALSE(FingerprintDatabase::parse_db_line("v2:algo:something").valid()); + ASSERT_FALSE(FingerprintDatabase::parse_db_line("v2:algo:01020304abc").valid()); + ASSERT_FALSE(FingerprintDatabase::parse_db_line("v2:algo:01020304ZZ").valid()); + ASSERT_EQ(FingerprintDatabase::parse_db_line("v2:algo:01020304ab"), + (FingerprintData{"algo", {1, 2, 3, 4, 0xab}})); +} + +TEST(FingerprintDatabase, read) +{ + std::istringstream stream; + stream.str(R"( +v2:algo1:01020304ab +v2:algo2:03040506ab +AB:CD:EF:00:01:02:03:04:05:06:07:08:09:10:11:12:13:14:15:16 +)"); + FingerprintDatabase db; + db.read_stream(stream); + + std::vector expected = { + { "algo1", { 1, 2, 3, 4, 0xab } }, + { "algo2", { 3, 4, 5, 6, 0xab } }, + { "sha1", { 0xab, 0xcd, 0xef, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 } }, + }; + ASSERT_EQ(db.fingerprints(), expected); +} + +TEST(FingerprintDatabase, write) +{ + std::ostringstream stream; + + FingerprintDatabase db; + db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } }); + db.add_trusted({ "algo2", { 3, 4, 5, 6, 0xab } }); + db.write_stream(stream); + + ASSERT_EQ(stream.str(), R"(v2:algo1:01020304ab +v2:algo2:03040506ab +)"); +} + +TEST(FingerprintDatabase, clear) +{ + FingerprintDatabase db; + db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } }); + db.clear(); + ASSERT_TRUE(db.fingerprints().empty()); +} + +TEST(FingerprintDatabase, add_trusted_no_duplicates) +{ + FingerprintDatabase db; + db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } }); + db.add_trusted({ "algo2", { 3, 4, 5, 6, 0xab } }); + db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } }); + ASSERT_EQ(db.fingerprints().size(), 2); +} + +TEST(FingerprintDatabase, is_trusted) +{ + FingerprintDatabase db; + db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } }); + ASSERT_TRUE(db.is_trusted({ "algo1", { 1, 2, 3, 4, 0xab } })); + ASSERT_FALSE(db.is_trusted({ "algo2", { 1, 2, 3, 4, 0xab } })); + ASSERT_FALSE(db.is_trusted({ "algo1", { 1, 2, 3, 4, 0xac } })); +} + +} // namespace barrier From be8ba0d13248fd6d51382f524b1992c380d21887 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:45 +0200 Subject: [PATCH 18/24] gui: Use new FingerprintDatabase to handle fingerprints --- src/gui/CMakeLists.txt | 2 - src/gui/src/Fingerprint.cpp | 147 ---------------------- src/gui/src/Fingerprint.h | 42 ------- src/gui/src/MainWindow.cpp | 50 ++++++-- src/gui/src/SslCertificate.cpp | 28 +++-- src/gui/src/SslCertificate.h | 4 +- src/lib/common/DataDirectories.h | 4 + src/lib/common/DataDirectories_static.cpp | 25 ++++ 8 files changed, 87 insertions(+), 215 deletions(-) delete mode 100644 src/gui/src/Fingerprint.cpp delete mode 100644 src/gui/src/Fingerprint.h diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 49557352..fb7678f2 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -29,7 +29,6 @@ set(GUI_SOURCE_FILES src/CommandProcess.cpp src/DataDownloader.cpp src/DisplayIsValid.cpp - src/Fingerprint.cpp src/HotkeyDialog.cpp src/IpcClient.cpp src/Ipc.cpp @@ -70,7 +69,6 @@ set(GUI_HEADER_FILES src/DataDownloader.h src/DisplayIsValid.h src/ElevateMode.h - src/Fingerprint.h src/HotkeyDialog.h src/IpcClient.h src/Ipc.h diff --git a/src/gui/src/Fingerprint.cpp b/src/gui/src/Fingerprint.cpp deleted file mode 100644 index cc1ce3bf..00000000 --- a/src/gui/src/Fingerprint.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2015-2016 Symless 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 LICENSE 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 . - */ - -#include "Fingerprint.h" - -#include "common/DataDirectories.h" - -#include -#include - -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(); - for (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() -{ - auto profileDir = QString::fromStdString(DataDirectories::profile()); - - 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); -} diff --git a/src/gui/src/Fingerprint.h b/src/gui/src/Fingerprint.h deleted file mode 100644 index 5a38d201..00000000 --- a/src/gui/src/Fingerprint.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2015-2016 Symless 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 LICENSE 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 . - */ - -#pragma once - -#include - -class Fingerprint -{ -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; - - static Fingerprint local(); - static Fingerprint trustedServers(); - static Fingerprint trustedClients(); - static QString directoryPath(); - static void persistDirectory(); - -private: - Fingerprint(const QString& filename); - - QString m_Filename; -}; diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 9072b864..02499a12 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -20,7 +20,6 @@ #include "MainWindow.h" -#include "Fingerprint.h" #include "AboutDialog.h" #include "ServerConfigDialog.h" #include "SettingsDialog.h" @@ -31,7 +30,10 @@ #include "ProcessorArch.h" #include "SslCertificate.h" #include "ShutdownCh.h" +#include "base/String.h" #include "common/DataDirectories.h" +#include "net/FingerprintDatabase.h" +#include "net/SecureUtils.h" #include #include @@ -417,11 +419,21 @@ void MainWindow::checkFingerprint(const QString& line) return; } - QString fingerprint = fingerprintRegex.cap(1); - if (Fingerprint::trustedServers().isTrusted(fingerprint)) { + barrier::FingerprintData fingerprint = { + barrier::fingerprint_type_to_string(barrier::FingerprintType::SHA1), + barrier::string::from_hex(fingerprintRegex.cap(1).toStdString()) + }; + + auto db_path = DataDirectories::trusted_servers_ssl_fingerprints_path(); + + barrier::FingerprintDatabase db; + db.read(db_path); + if (db.is_trusted(fingerprint)) { return; } + auto formatted_fingerprint = barrier::format_ssl_fingerprint(fingerprint.data); + static bool messageBoxAlreadyShown = false; if (!messageBoxAlreadyShown) { @@ -440,12 +452,13 @@ void MainWindow::checkFingerprint(const QString& line) "To automatically trust this fingerprint for future " "connections, click Yes. To reject this fingerprint and " "disconnect from the server, click No.") - .arg(fingerprint), + .arg(QString::fromStdString(formatted_fingerprint)), QMessageBox::Yes | QMessageBox::No); if (fingerprintReply == QMessageBox::Yes) { // restart core process after trusting fingerprint. - Fingerprint::trustedServers().trust(fingerprint); + db.add_trusted(fingerprint); + db.write(db_path); startBarrier(); } @@ -965,12 +978,29 @@ void MainWindow::updateSSLFingerprint() }); m_pSslCertificate->generateCertificate(); } - if (m_AppConfig->getCryptoEnabled() && Fingerprint::local().fileExists()) { - m_pLabelLocalFingerprint->setText(Fingerprint::local().readFirst()); - m_pLabelLocalFingerprint->setTextInteractionFlags(Qt::TextSelectableByMouse); - } else { - m_pLabelLocalFingerprint->setText("Disabled"); + + m_pLabelLocalFingerprint->setText("Disabled"); + + if (!m_AppConfig->getCryptoEnabled()) { + return; } + + auto local_path = DataDirectories::local_ssl_fingerprints_path(); + if (!QFile::exists(QString::fromStdString(local_path))) { + return; + } + + barrier::FingerprintDatabase db; + db.read(local_path); + if (db.fingerprints().empty()) { + return; + } + + const auto& fingerprint = db.fingerprints().front(); + auto formatted_fingerprint = barrier::format_ssl_fingerprint(fingerprint.data); + + m_pLabelLocalFingerprint->setText(QString::fromStdString(formatted_fingerprint)); + m_pLabelLocalFingerprint->setTextInteractionFlags(Qt::TextSelectableByMouse); } void MainWindow::on_m_pGroupClient_toggled(bool on) diff --git a/src/gui/src/SslCertificate.cpp b/src/gui/src/SslCertificate.cpp index ac70d01a..4242df56 100644 --- a/src/gui/src/SslCertificate.cpp +++ b/src/gui/src/SslCertificate.cpp @@ -16,8 +16,8 @@ */ #include "SslCertificate.h" -#include "Fingerprint.h" #include "common/DataDirectories.h" +#include "net/FingerprintDatabase.h" #include "net/SecureUtils.h" #include @@ -44,17 +44,17 @@ SslCertificate::SslCertificate(QObject *parent) : void SslCertificate::generateCertificate() { - auto filename = QString::fromStdString(getCertificatePath()); + auto cert_path = getCertificatePath(); - QFile file(filename); - if (!file.exists() || !isCertificateValid(filename)) { + QFile file(QString::fromStdString(cert_path)); + if (!file.exists() || !isCertificateValid(cert_path)) { QDir sslDir(QString::fromStdString(getCertificateDirectory())); if (!sslDir.exists()) { sslDir.mkpath("."); } try { - barrier::generate_pem_self_signed_cert(filename.toStdString()); + barrier::generate_pem_self_signed_cert(cert_path); } catch (const std::exception& e) { emit error(QString("SSL tool failed: %1").arg(e.what())); return; @@ -63,18 +63,22 @@ void SslCertificate::generateCertificate() emit info(tr("SSL certificate generated.")); } - generateFingerprint(filename); + generateFingerprint(cert_path); emit generateFinished(); } -void SslCertificate::generateFingerprint(const QString& certificateFilename) +void SslCertificate::generateFingerprint(const std::string& cert_path) { try { - auto fingerprint = barrier::get_pem_file_cert_fingerprint(certificateFilename.toStdString(), + auto fingerprint = barrier::get_pem_file_cert_fingerprint(cert_path, barrier::FingerprintType::SHA1); - Fingerprint::local().trust(QString::fromStdString( - barrier::format_ssl_fingerprint(fingerprint)), false); + + auto local_path = DataDirectories::local_ssl_fingerprints_path(); + barrier::FingerprintDatabase db; + db.add_trusted(barrier::FingerprintData{"sha1", fingerprint}); + db.write(local_path); + emit info(tr("SSL fingerprint generated.")); } catch (const std::exception& e) { emit error(tr("Failed to find SSL fingerprint.") + e.what()); @@ -91,7 +95,7 @@ std::string SslCertificate::getCertificateDirectory() return m_ProfileDir + QDir::separator().toLatin1() + kSslDir; } -bool SslCertificate::isCertificateValid(const QString& path) +bool SslCertificate::isCertificateValid(const std::string& path) { OpenSSL_add_all_algorithms(); ERR_load_BIO_strings(); @@ -99,7 +103,7 @@ bool SslCertificate::isCertificateValid(const QString& path) BIO* bio = BIO_new(BIO_s_file()); - auto ret = BIO_read_filename(bio, path.toStdString().c_str()); + auto ret = BIO_read_filename(bio, path.c_str()); if (!ret) { emit info(tr("Could not read from default certificate file.")); BIO_free_all(bio); diff --git a/src/gui/src/SslCertificate.h b/src/gui/src/SslCertificate.h index 2fe807a2..7f77771a 100644 --- a/src/gui/src/SslCertificate.h +++ b/src/gui/src/SslCertificate.h @@ -37,12 +37,12 @@ signals: private: std::pair runTool(const QStringList& args); - void generateFingerprint(const QString& certificateFilename); + void generateFingerprint(const std::string& cert_path); std::string getCertificatePath(); std::string getCertificateDirectory(); - bool isCertificateValid(const QString& path); + bool isCertificateValid(const std::string& path); private: std::string m_ProfileDir; }; diff --git a/src/lib/common/DataDirectories.h b/src/lib/common/DataDirectories.h index 783ff138..4489ac24 100644 --- a/src/lib/common/DataDirectories.h +++ b/src/lib/common/DataDirectories.h @@ -31,6 +31,10 @@ public: static const std::string& systemconfig(); static const std::string& systemconfig(const std::string& path); + static std::string ssl_fingerprints_path(); + static std::string local_ssl_fingerprints_path(); + static std::string trusted_servers_ssl_fingerprints_path(); + static std::string trusted_clients_ssl_fingerprints_path(); private: static std::string _profile; static std::string _global; diff --git a/src/lib/common/DataDirectories_static.cpp b/src/lib/common/DataDirectories_static.cpp index 48dccb68..5e28e055 100644 --- a/src/lib/common/DataDirectories_static.cpp +++ b/src/lib/common/DataDirectories_static.cpp @@ -21,3 +21,28 @@ std::string DataDirectories::_profile; std::string DataDirectories::_global; std::string DataDirectories::_systemconfig; + +static const char kFingerprintsDirName[] = "SSL/Fingerprints"; +static const char kFingerprintsLocalFilename[] = "Local.txt"; +static const char kFingerprintsTrustedServersFilename[] = "TrustedServers.txt"; +static const char kFingerprintsTrustedClientsFilename[] = "TrustedClients.txt"; + +std::string DataDirectories::ssl_fingerprints_path() +{ + return profile() + "/" + kFingerprintsDirName; +} + +std::string DataDirectories::local_ssl_fingerprints_path() +{ + return ssl_fingerprints_path() + "/" + kFingerprintsLocalFilename; +} + +std::string DataDirectories::trusted_servers_ssl_fingerprints_path() +{ + return ssl_fingerprints_path() + "/" + kFingerprintsTrustedServersFilename; +} + +std::string DataDirectories::trusted_clients_ssl_fingerprints_path() +{ + return ssl_fingerprints_path() + "/" + kFingerprintsTrustedClientsFilename; +} From 50534ecb43e7cf0ff827e296b7b3784bff3a0bd5 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:46 +0200 Subject: [PATCH 19/24] lib/net: Use new FingerprintDatabase to handle fingerprints --- src/lib/net/SecureSocket.cpp | 55 +++++++++++++----------------------- 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/src/lib/net/SecureSocket.cpp b/src/lib/net/SecureSocket.cpp index 286394c4..c38c5315 100644 --- a/src/lib/net/SecureSocket.cpp +++ b/src/lib/net/SecureSocket.cpp @@ -27,6 +27,7 @@ #include "base/String.h" #include "common/DataDirectories.h" #include "io/fstream.h" +#include "net/FingerprintDatabase.h" #include #include @@ -48,11 +49,6 @@ enum { kMsgSize = 128 }; -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; @@ -670,46 +666,33 @@ SecureSocket::verifyCertFingerprint() return false; } - auto fingerprint = barrier::format_ssl_fingerprint(fingerprint_raw); - LOG((CLOG_NOTE "server fingerprint: %s", fingerprint.c_str())); + LOG((CLOG_NOTE "server fingerprint: %s", + barrier::format_ssl_fingerprint(fingerprint_raw).c_str())); - std::string trustedServersFilename; - trustedServersFilename = barrier::string::sprintf( - "%s/%s/%s", - DataDirectories::profile().c_str(), - kFingerprintDirName, - kFingerprintTrustedServersFilename); + auto fingerprint_db_path = DataDirectories::trusted_servers_ssl_fingerprints_path(); // Provide debug hint as to what file is being used to verify fingerprint trust - LOG((CLOG_NOTE "trustedServersFilename: %s", trustedServersFilename.c_str() )); + LOG((CLOG_NOTE "fingerprint_db_path: %s", fingerprint_db_path.c_str())); - // check if this fingerprint exist - std::string fileLine; - std::ifstream file; - barrier::open_utf8_path(file, trustedServersFilename); + barrier::FingerprintDatabase db; + db.read(fingerprint_db_path); - if (!file.is_open()) { - LOG((CLOG_NOTE "Unable to open trustedServersFile: %s", trustedServersFilename.c_str() )); + if (!db.fingerprints().empty()) { + LOG((CLOG_NOTE "Read %d fingerprints from: %s", db.fingerprints().size(), + fingerprint_db_path.c_str())); } else { - LOG((CLOG_NOTE "Opened trustedServersFilename: %s", trustedServersFilename.c_str() )); + LOG((CLOG_NOTE "Could not read fingerprints from: %s", + fingerprint_db_path.c_str())); } - bool isValid = false; - while (!file.eof() && file.is_open()) { - getline(file,fileLine); - if (!fileLine.empty()) { - if (fileLine.compare(fingerprint) == 0) { - LOG((CLOG_NOTE "Fingerprint matches trusted fingerprint")); - isValid = true; - break; - } else { - LOG((CLOG_NOTE "Fingerprint does not match trusted fingerprint")); - } - } + barrier::FingerprintData fingerprint{"sha1", fingerprint_raw}; + if (db.is_trusted(fingerprint)) { + LOG((CLOG_NOTE "Fingerprint matches trusted fingerprint")); + return true; + } else { + LOG((CLOG_NOTE "Fingerprint does not match trusted fingerprint")); + return false; } - - file.close(); - return isValid; } MultiplexerJobStatus SecureSocket::serviceConnect(ISocketMultiplexerJob* job, From 7cced74119fc2f4e96f4a894132090057d28455a Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:47 +0200 Subject: [PATCH 20/24] lib/net: Use FingerprintData to represent fingerprints --- src/gui/src/SslCertificate.cpp | 2 +- ...{FingerprintType.h => FingerprintData.cpp} | 24 +++++----- src/lib/net/FingerprintData.h | 46 +++++++++++++++++++ src/lib/net/FingerprintDatabase.cpp | 5 -- src/lib/net/FingerprintDatabase.h | 11 +---- src/lib/net/SecureSocket.cpp | 9 ++-- src/lib/net/SecureUtils.cpp | 8 ++-- src/lib/net/SecureUtils.h | 7 ++- 8 files changed, 70 insertions(+), 42 deletions(-) rename src/lib/net/{FingerprintType.h => FingerprintData.cpp} (74%) create mode 100644 src/lib/net/FingerprintData.h diff --git a/src/gui/src/SslCertificate.cpp b/src/gui/src/SslCertificate.cpp index 4242df56..a96a3e30 100644 --- a/src/gui/src/SslCertificate.cpp +++ b/src/gui/src/SslCertificate.cpp @@ -76,7 +76,7 @@ void SslCertificate::generateFingerprint(const std::string& cert_path) auto local_path = DataDirectories::local_ssl_fingerprints_path(); barrier::FingerprintDatabase db; - db.add_trusted(barrier::FingerprintData{"sha1", fingerprint}); + db.add_trusted(fingerprint); db.write(local_path); emit info(tr("SSL fingerprint generated.")); diff --git a/src/lib/net/FingerprintType.h b/src/lib/net/FingerprintData.cpp similarity index 74% rename from src/lib/net/FingerprintType.h rename to src/lib/net/FingerprintData.cpp index 4e58e9f6..f7acbd28 100644 --- a/src/lib/net/FingerprintType.h +++ b/src/lib/net/FingerprintData.cpp @@ -15,20 +15,20 @@ along with this program. If not, see . */ -#ifndef BARRIER_LIB_NET_FINGERPRINT_TYPE_H -#define BARRIER_LIB_NET_FINGERPRINT_TYPE_H - -#include +#include "base/String.h" +#include "FingerprintDatabase.h" +#include "io/fstream.h" +#include +#include namespace barrier { -enum FingerprintType { - INVALID, - SHA1, // deprecated - SHA256, -}; +bool FingerprintData::operator==(const FingerprintData& other) const +{ + return algorithm == other.algorithm && data == other.data; +} -inline const char* fingerprint_type_to_string(FingerprintType type) +const char* fingerprint_type_to_string(FingerprintType type) { switch (type) { case FingerprintType::INVALID: return "invalid"; @@ -38,7 +38,7 @@ inline const char* fingerprint_type_to_string(FingerprintType type) return "invalid"; } -inline FingerprintType fingerprint_type_from_string(const std::string& type) +FingerprintType fingerprint_type_from_string(const std::string& type) { if (type == "sha1") { return FingerprintType::SHA1; @@ -50,5 +50,3 @@ inline FingerprintType fingerprint_type_from_string(const std::string& type) } } // namespace barrier - -#endif // BARRIER_LIB_NET_FINGERPRINT_TYPE_H diff --git a/src/lib/net/FingerprintData.h b/src/lib/net/FingerprintData.h new file mode 100644 index 00000000..938a6953 --- /dev/null +++ b/src/lib/net/FingerprintData.h @@ -0,0 +1,46 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + 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 LICENSE 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 . +*/ + +#ifndef BARRIER_LIB_NET_FINGERPRINT_DATA_H +#define BARRIER_LIB_NET_FINGERPRINT_DATA_H + +#include +#include + +namespace barrier { + +enum FingerprintType { + INVALID, + SHA1, // deprecated + SHA256, +}; + +struct FingerprintData { + std::string algorithm; + std::vector data; + + bool valid() const { return !algorithm.empty(); } + + bool operator==(const FingerprintData& other) const; +}; + +const char* fingerprint_type_to_string(FingerprintType type); +FingerprintType fingerprint_type_from_string(const std::string& type); + +} // namespace barrier + +#endif // BARRIER_LIB_NET_FINGERPRINT_TYPE_H diff --git a/src/lib/net/FingerprintDatabase.cpp b/src/lib/net/FingerprintDatabase.cpp index cdc3ad32..3dcbaee6 100644 --- a/src/lib/net/FingerprintDatabase.cpp +++ b/src/lib/net/FingerprintDatabase.cpp @@ -23,11 +23,6 @@ namespace barrier { -bool FingerprintData::operator==(const FingerprintData& other) const -{ - return algorithm == other.algorithm && data == other.data; -} - void FingerprintDatabase::read(const std::string& path) { std::ifstream file; diff --git a/src/lib/net/FingerprintDatabase.h b/src/lib/net/FingerprintDatabase.h index 36ab39ce..4a17696e 100644 --- a/src/lib/net/FingerprintDatabase.h +++ b/src/lib/net/FingerprintDatabase.h @@ -18,22 +18,13 @@ #ifndef BARRIER_LIB_NET_FINGERPRINT_DATABASE_H #define BARRIER_LIB_NET_FINGERPRINT_DATABASE_H -#include "FingerprintType.h" +#include "FingerprintData.h" #include #include #include namespace barrier { -struct FingerprintData { - std::string algorithm; - std::vector data; - - bool valid() const { return !algorithm.empty(); } - - bool operator==(const FingerprintData& other) const; -}; - class FingerprintDatabase { public: void read(const std::string& path); diff --git a/src/lib/net/SecureSocket.cpp b/src/lib/net/SecureSocket.cpp index c38c5315..245f5287 100644 --- a/src/lib/net/SecureSocket.cpp +++ b/src/lib/net/SecureSocket.cpp @@ -657,17 +657,17 @@ bool SecureSocket::verifyCertFingerprint() { // calculate received certificate fingerprint - std::vector fingerprint_raw; + barrier::FingerprintData fingerprint; try { - fingerprint_raw = barrier::get_ssl_cert_fingerprint(SSL_get_peer_certificate(m_ssl->m_ssl), - barrier::FingerprintType::SHA1); + fingerprint = barrier::get_ssl_cert_fingerprint(SSL_get_peer_certificate(m_ssl->m_ssl), + barrier::FingerprintType::SHA1); } catch (const std::exception& e) { LOG((CLOG_ERR "%s", e.what())); return false; } LOG((CLOG_NOTE "server fingerprint: %s", - barrier::format_ssl_fingerprint(fingerprint_raw).c_str())); + barrier::format_ssl_fingerprint(fingerprint.data).c_str())); auto fingerprint_db_path = DataDirectories::trusted_servers_ssl_fingerprints_path(); @@ -685,7 +685,6 @@ SecureSocket::verifyCertFingerprint() fingerprint_db_path.c_str())); } - barrier::FingerprintData fingerprint{"sha1", fingerprint_raw}; if (db.is_trusted(fingerprint)) { LOG((CLOG_NOTE "Fingerprint matches trusted fingerprint")); return true; diff --git a/src/lib/net/SecureUtils.cpp b/src/lib/net/SecureUtils.cpp index 4b081f66..a9852558 100644 --- a/src/lib/net/SecureUtils.cpp +++ b/src/lib/net/SecureUtils.cpp @@ -15,6 +15,7 @@ along with this program. If not, see . */ +#include "FingerprintDatabase.h" #include "SecureUtils.h" #include "base/String.h" #include "base/finally.h" @@ -59,7 +60,7 @@ std::string format_ssl_fingerprint(const std::vector& fingerprint, bool return result; } -std::vector get_ssl_cert_fingerprint(X509* cert, FingerprintType type) +FingerprintData get_ssl_cert_fingerprint(X509* cert, FingerprintType type) { if (!cert) { throw std::runtime_error("certificate is null"); @@ -77,11 +78,10 @@ std::vector get_ssl_cert_fingerprint(X509* cert, FingerprintType t std::vector digest_vec; digest_vec.assign(reinterpret_cast(digest), reinterpret_cast(digest) + digest_length); - return digest_vec; + return {fingerprint_type_to_string(type), digest_vec}; } -std::vector get_pem_file_cert_fingerprint(const std::string& path, - FingerprintType type) +FingerprintData get_pem_file_cert_fingerprint(const std::string& path, FingerprintType type) { auto fp = fopen_utf8_path(path, "r"); if (!fp) { diff --git a/src/lib/net/SecureUtils.h b/src/lib/net/SecureUtils.h index ace0d45a..c6361419 100644 --- a/src/lib/net/SecureUtils.h +++ b/src/lib/net/SecureUtils.h @@ -18,7 +18,7 @@ #ifndef BARRIER_LIB_NET_SECUREUTILS_H #define BARRIER_LIB_NET_SECUREUTILS_H -#include "FingerprintType.h" +#include "FingerprintData.h" #include #include #include @@ -29,10 +29,9 @@ namespace barrier { std::string format_ssl_fingerprint(const std::vector& fingerprint, bool separator = true); -std::vector get_ssl_cert_fingerprint(X509* cert, FingerprintType type); +FingerprintData get_ssl_cert_fingerprint(X509* cert, FingerprintType type); -std::vector get_pem_file_cert_fingerprint(const std::string& path, - FingerprintType type); +FingerprintData get_pem_file_cert_fingerprint(const std::string& path, FingerprintType type); void generate_pem_self_signed_cert(const std::string& path); From a238b27879229407cbb08fc4201dbe8533bf64b8 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:49 +0200 Subject: [PATCH 21/24] gui: Simplify isCertificateValid() --- src/gui/src/SslCertificate.cpp | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/src/gui/src/SslCertificate.cpp b/src/gui/src/SslCertificate.cpp index a96a3e30..39c63674 100644 --- a/src/gui/src/SslCertificate.cpp +++ b/src/gui/src/SslCertificate.cpp @@ -17,6 +17,8 @@ #include "SslCertificate.h" #include "common/DataDirectories.h" +#include "base/finally.h" +#include "io/fstream.h" #include "net/FingerprintDatabase.h" #include "net/SecureUtils.h" @@ -98,39 +100,32 @@ std::string SslCertificate::getCertificateDirectory() bool SslCertificate::isCertificateValid(const std::string& path) { OpenSSL_add_all_algorithms(); - ERR_load_BIO_strings(); ERR_load_crypto_strings(); - BIO* bio = BIO_new(BIO_s_file()); - - auto ret = BIO_read_filename(bio, path.c_str()); - if (!ret) { + auto fp = barrier::fopen_utf8_path(path, "r"); + if (!fp) { emit info(tr("Could not read from default certificate file.")); - BIO_free_all(bio); return false; } + auto file_close = barrier::finally([fp]() { std::fclose(fp); }); - X509* cert = PEM_read_bio_X509(bio, NULL, 0, NULL); + auto* cert = PEM_read_X509(fp, nullptr, nullptr, nullptr); if (!cert) { emit info(tr("Error loading default certificate file to memory.")); - BIO_free_all(bio); return false; } + auto cert_free = barrier::finally([cert]() { X509_free(cert); }); - EVP_PKEY* pubkey = X509_get_pubkey(cert); + auto* pubkey = X509_get_pubkey(cert); if (!pubkey) { emit info(tr("Default certificate key file does not contain valid public key")); - X509_free(cert); - BIO_free_all(bio); return false; } + auto pubkey_free = barrier::finally([pubkey]() { EVP_PKEY_free(pubkey); }); auto type = EVP_PKEY_type(EVP_PKEY_id(pubkey)); if (type != EVP_PKEY_RSA && type != EVP_PKEY_DSA) { emit info(tr("Public key in default certificate key file is not RSA or DSA")); - EVP_PKEY_free(pubkey); - X509_free(cert); - BIO_free_all(bio); return false; } @@ -138,14 +133,8 @@ bool SslCertificate::isCertificateValid(const std::string& path) if (bits < 2048) { // We could have small keys in old barrier installations emit info(tr("Public key in default certificate key file is too small.")); - EVP_PKEY_free(pubkey); - X509_free(cert); - BIO_free_all(bio); return false; } - EVP_PKEY_free(pubkey); - X509_free(cert); - BIO_free_all(bio); return true; } From b7757fbd688cc9db70be5bbb590f5fa075b1700c Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:50 +0200 Subject: [PATCH 22/24] lib/net: Implement a way to generate fingerprint randomart The code has been copied from OpenSSH. --- src/lib/net/SecureUtils.cpp | 140 +++++++++++++++++++- src/lib/net/SecureUtils.h | 2 + src/test/unittests/net/SecureUtilsTests.cpp | 40 ++++++ 3 files changed, 181 insertions(+), 1 deletion(-) diff --git a/src/lib/net/SecureUtils.cpp b/src/lib/net/SecureUtils.cpp index a9852558..c7e0a82d 100644 --- a/src/lib/net/SecureUtils.cpp +++ b/src/lib/net/SecureUtils.cpp @@ -13,9 +13,36 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . + + ----------------------------------------------------------------------- + create_fingerprint_randomart() has been taken from the OpenSSH project. + Copyright information follows. + + Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. + Copyright (c) 2008 Alexander von Gernler. All rights reserved. + Copyright (c) 2010,2011 Damien Miller. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "FingerprintDatabase.h" #include "SecureUtils.h" #include "base/String.h" #include "base/finally.h" @@ -25,7 +52,9 @@ #include #include #include +#include #include +#include #include namespace barrier { @@ -142,4 +171,113 @@ void generate_pem_self_signed_cert(const std::string& path) PEM_write_X509(fp, cert); } +/* + Draw an ASCII-Art representing the fingerprint so human brain can + profit from its built-in pattern recognition ability. + This technique is called "random art" and can be found in some + scientific publications like this original paper: + + "Hash Visualization: a New Technique to improve Real-World Security", + Perrig A. and Song D., 1999, International Workshop on Cryptographic + Techniques and E-Commerce (CrypTEC '99) + sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf + + The subject came up in a talk by Dan Kaminsky, too. + + If you see the picture is different, the key is different. + If the picture looks the same, you still know nothing. + + The algorithm used here is a worm crawling over a discrete plane, + leaving a trace (augmenting the field) everywhere it goes. + Movement is taken from dgst_raw 2bit-wise. Bumping into walls + makes the respective movement vector be ignored for this turn. + Graphs are not unambiguous, because circles in graphs can be +walked in either direction. + */ + +/* + Field sizes for the random art. Have to be odd, so the starting point + can be in the exact middle of the picture, and FLDBASE should be >=8 . + Else pictures would be too dense, and drawing the frame would + fail, too, because the key type would not fit in anymore. +*/ +#define FLDBASE 8 +#define FLDSIZE_Y (FLDBASE + 1) +#define FLDSIZE_X (FLDBASE * 2 + 1) + +std::string create_fingerprint_randomart(const std::vector& dgst_raw) +{ + /* + * Chars to be used after each other every time the worm + * intersects with itself. Matter of taste. + */ + const char* augmentation_string = " .o+=*BOX@%&#/^SE"; + char *p; + std::uint8_t field[FLDSIZE_X][FLDSIZE_Y]; + std::size_t i; + std::uint32_t b; + int x, y; + std::size_t len = strlen(augmentation_string) - 1; + + std::vector retval; + retval.reserve((FLDSIZE_X + 3) * (FLDSIZE_Y + 2)); + + auto add_char = [&retval](char ch) { retval.push_back(ch); }; + + /* initialize field */ + std::memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char)); + x = FLDSIZE_X / 2; + y = FLDSIZE_Y / 2; + + /* process raw key */ + for (i = 0; i < dgst_raw.size(); i++) { + /* each byte conveys four 2-bit move commands */ + int input = dgst_raw[i]; + for (b = 0; b < 4; b++) { + /* evaluate 2 bit, rest is shifted later */ + x += (input & 0x1) ? 1 : -1; + y += (input & 0x2) ? 1 : -1; + + /* assure we are still in bounds */ + x = std::max(x, 0); + y = std::max(y, 0); + x = std::min(x, FLDSIZE_X - 1); + y = std::min(y, FLDSIZE_Y - 1); + + /* augment the field */ + if (field[x][y] < len - 2) + field[x][y]++; + input = input >> 2; + } + } + + /* mark starting point and end point*/ + field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1; + field[x][y] = len; + + /* output upper border */ + add_char('+'); + for (i = 0; i < FLDSIZE_X; i++) + add_char('-'); + add_char('+'); + add_char('\n'); + + /* output content */ + for (y = 0; y < FLDSIZE_Y; y++) { + add_char('|'); + for (x = 0; x < FLDSIZE_X; x++) + add_char(augmentation_string[std::min(field[x][y], len)]); + add_char('|'); + add_char('\n'); + } + + /* output lower border */ + add_char('+'); + for (i = 0; i < FLDSIZE_X; i++) + add_char('-'); + add_char('+'); + + return std::string{retval.data(), retval.size()}; +} + } // namespace barrier diff --git a/src/lib/net/SecureUtils.h b/src/lib/net/SecureUtils.h index c6361419..7525381f 100644 --- a/src/lib/net/SecureUtils.h +++ b/src/lib/net/SecureUtils.h @@ -35,6 +35,8 @@ FingerprintData get_pem_file_cert_fingerprint(const std::string& path, Fingerpri void generate_pem_self_signed_cert(const std::string& path); +std::string create_fingerprint_randomart(const std::vector& dgst_raw); + } // namespace barrier #endif // BARRIER_LIB_NET_SECUREUTILS_H diff --git a/src/test/unittests/net/SecureUtilsTests.cpp b/src/test/unittests/net/SecureUtilsTests.cpp index c2394bf9..0cce693a 100644 --- a/src/test/unittests/net/SecureUtilsTests.cpp +++ b/src/test/unittests/net/SecureUtilsTests.cpp @@ -30,4 +30,44 @@ TEST(SecureUtilsTest, FormatSslFingerprintHexWithSeparators) "CA:B2:8E:49:25:94:90:25:26:05:8D:AF:63:ED:2E:30"); } +TEST(SecureUtilsTest, CreateFingerprintRandomArt) +{ + ASSERT_EQ(create_fingerprint_randomart(generate_pseudo_random_bytes(0, 32)), + "+-----------------+\n" + "|*X+. . |\n" + "|*oo + |\n" + "| + = |\n" + "| B . . |\n" + "|.+... o S |\n" + "|E+ ++. . |\n" + "|B*++.. . |\n" + "|+o*o o . |\n" + "|+o*Bo . |\n" + "+-----------------+"); + ASSERT_EQ(create_fingerprint_randomart(generate_pseudo_random_bytes(1, 32)), + "+-----------------+\n" + "| .oo+ . .B=. |\n" + "| .o.+ . o o.= |\n" + "|o..+.. o . E * |\n" + "|oo..+ . * * |\n" + "|B o.....S. o . |\n" + "|+=o..... |\n" + "| + + . |\n" + "|o. .. |\n" + "|..o.. |\n" + "+-----------------+"); + ASSERT_EQ(create_fingerprint_randomart(generate_pseudo_random_bytes(2, 32)), + "+-----------------+\n" + "| ... .o.o.|\n" + "| o .=.E|\n" + "| . + o ...+.|\n" + "| * o = o ... |\n" + "| * + S & . |\n" + "| = + % @ |\n" + "| . . = X o |\n" + "| . . O . |\n" + "| . + |\n" + "+-----------------+"); +} + } // namespace barrier From c7e6fc6c7e51877fa0b98c396e1240d7d7ccd4c2 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:51 +0200 Subject: [PATCH 23/24] gui: Set the size of the window to the size of the contents --- src/gui/src/MainWindow.cpp | 8 ++++++++ src/gui/src/MainWindow.h | 1 + src/gui/src/MainWindowBase.ui | 17 +++-------------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 02499a12..b1d6fc83 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -938,6 +938,14 @@ void MainWindow::changeEvent(QEvent* event) QMainWindow::changeEvent(event); } +bool MainWindow::event(QEvent* event) +{ + if (event->type() == QEvent::LayoutRequest) { + setFixedSize(sizeHint()); + } + return QMainWindow::event(event); +} + void MainWindow::updateZeroconfService() { QMutexLocker locker(&m_UpdateZeroconfMutex); diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index 131b31ce..9a6c615d 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -157,6 +157,7 @@ public slots: void stopService(); void stopDesktop(); void changeEvent(QEvent* event); + bool event(QEvent* event); void retranslateMenuBar(); #if defined(Q_OS_WIN) bool isServiceRunning(QString name); diff --git a/src/gui/src/MainWindowBase.ui b/src/gui/src/MainWindowBase.ui index 117405ca..3e31ac9f 100644 --- a/src/gui/src/MainWindowBase.ui +++ b/src/gui/src/MainWindowBase.ui @@ -2,31 +2,20 @@ MainWindowBase - - - 0 - 0 - 600 - 550 - - 0 0 - - - 600 - 0 - - Barrier + + QLayout::SetFixedSize + From a428b61c7dfbd120f00f97c6a656ea3e858736ea Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Mon, 1 Nov 2021 02:52:52 +0200 Subject: [PATCH 24/24] gui: Add support for SHA256 fingerprints For the time being both SHA1 and SHA256 fingerprints will be shown in the UI. This allows users to verify new connections between old and new versions of Barrier. After the initial verification we use SHA256 fingerprints. The issue has been reported by Matthias Gerstner . --- .../fingerprint-randomart.feature | 3 + doc/newsfragments/sha256-fingerprints.bugfix | 4 + src/gui/src/MainWindow.cpp | 70 +++++++++++++--- src/gui/src/MainWindow.h | 2 + src/gui/src/MainWindowBase.ui | 83 ++++++++++++++++++- src/gui/src/SslCertificate.cpp | 8 +- src/lib/net/SecureSocket.cpp | 17 ++-- src/lib/net/SecureUtils.cpp | 23 +++++ src/lib/net/SecureUtils.h | 1 + 9 files changed, 185 insertions(+), 26 deletions(-) create mode 100644 doc/newsfragments/fingerprint-randomart.feature create mode 100644 doc/newsfragments/sha256-fingerprints.bugfix diff --git a/doc/newsfragments/fingerprint-randomart.feature b/doc/newsfragments/fingerprint-randomart.feature new file mode 100644 index 00000000..9ffced93 --- /dev/null +++ b/doc/newsfragments/fingerprint-randomart.feature @@ -0,0 +1,3 @@ +Added support for randomart images for easier comparison of SSL +certificate fingerprints. The algorithm is identical to what +OpenSSH uses. diff --git a/doc/newsfragments/sha256-fingerprints.bugfix b/doc/newsfragments/sha256-fingerprints.bugfix new file mode 100644 index 00000000..a724c3b5 --- /dev/null +++ b/doc/newsfragments/sha256-fingerprints.bugfix @@ -0,0 +1,4 @@ +Barrier now uses SHA256 fingerprints for establishing security of encrypted SSL connections. +After upgrading client to new version the existing server fingerprint will need to be approved again. +Client and server will show both SHA1 and SHA256 server fingerprints to allow interoperability +with older versions of Barrier. diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index b1d6fc83..ccb5c196 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -158,9 +158,22 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : m_pComboServerList->hide(); m_pLabelPadlock->hide(); + frame_fingerprint_details->hide(); updateSSLFingerprint(); + connect(toolbutton_show_fingerprint, &QToolButton::clicked, [this](bool checked) + { + m_fingerprint_expanded = !m_fingerprint_expanded; + if (m_fingerprint_expanded) { + frame_fingerprint_details->show(); + toolbutton_show_fingerprint->setArrowType(Qt::ArrowType::UpArrow); + } else { + frame_fingerprint_details->hide(); + toolbutton_show_fingerprint->setArrowType(Qt::ArrowType::DownArrow); + } + }); + // resize window to smallest reasonable size resize(0, 0); } @@ -414,26 +427,32 @@ void MainWindow::checkConnected(const QString& line) void MainWindow::checkFingerprint(const QString& line) { - QRegExp fingerprintRegex(".*server fingerprint: ([A-F0-9:]+)"); + QRegExp fingerprintRegex(".*server fingerprint \\(SHA1\\): ([A-F0-9:]+) \\(SHA256\\): ([A-F0-9:]+)"); if (!fingerprintRegex.exactMatch(line)) { return; } - barrier::FingerprintData fingerprint = { + barrier::FingerprintData fingerprint_sha1 = { barrier::fingerprint_type_to_string(barrier::FingerprintType::SHA1), barrier::string::from_hex(fingerprintRegex.cap(1).toStdString()) }; + barrier::FingerprintData fingerprint_sha256 = { + barrier::fingerprint_type_to_string(barrier::FingerprintType::SHA256), + barrier::string::from_hex(fingerprintRegex.cap(2).toStdString()) + }; + auto db_path = DataDirectories::trusted_servers_ssl_fingerprints_path(); + // We compare only SHA256 fingerprints, but show both SHA1 and SHA256 so that the users can + // still verify fingerprints on old Barrier servers. This way the only time when we are exposed + // to SHA1 vulnerabilities is when the user is reconnecting again. barrier::FingerprintDatabase db; db.read(db_path); - if (db.is_trusted(fingerprint)) { + if (db.is_trusted(fingerprint_sha256)) { return; } - auto formatted_fingerprint = barrier::format_ssl_fingerprint(fingerprint.data); - static bool messageBoxAlreadyShown = false; if (!messageBoxAlreadyShown) { @@ -444,7 +463,11 @@ void MainWindow::checkFingerprint(const QString& line) QMessageBox::information( this, tr("Security question"), tr("Do you trust this fingerprint?\n\n" - "%1\n\n" + "SHA256:\n" + "%1\n" + "%2\n\n" + "SHA1 (obsolete, when using old Barrier server):\n" + "%3\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 " @@ -452,12 +475,15 @@ void MainWindow::checkFingerprint(const QString& line) "To automatically trust this fingerprint for future " "connections, click Yes. To reject this fingerprint and " "disconnect from the server, click No.") - .arg(QString::fromStdString(formatted_fingerprint)), + .arg(QString::fromStdString(barrier::format_ssl_fingerprint(fingerprint_sha256.data))) + .arg(QString::fromStdString( + barrier::create_fingerprint_randomart(fingerprint_sha256.data))) + .arg(QString::fromStdString(barrier::format_ssl_fingerprint(fingerprint_sha1.data))), QMessageBox::Yes | QMessageBox::No); if (fingerprintReply == QMessageBox::Yes) { // restart core process after trusting fingerprint. - db.add_trusted(fingerprint); + db.add_trusted(fingerprint_sha256); db.write(db_path); startBarrier(); } @@ -987,6 +1013,7 @@ void MainWindow::updateSSLFingerprint() m_pSslCertificate->generateCertificate(); } + toolbutton_show_fingerprint->setEnabled(false); m_pLabelLocalFingerprint->setText("Disabled"); if (!m_AppConfig->getCryptoEnabled()) { @@ -1000,15 +1027,32 @@ void MainWindow::updateSSLFingerprint() barrier::FingerprintDatabase db; db.read(local_path); - if (db.fingerprints().empty()) { + if (db.fingerprints().size() != 2) { return; } - const auto& fingerprint = db.fingerprints().front(); - auto formatted_fingerprint = barrier::format_ssl_fingerprint(fingerprint.data); + for (const auto& fingerprint : db.fingerprints()) { + if (fingerprint.algorithm == "sha1") { + auto fingerprint_str = barrier::format_ssl_fingerprint(fingerprint.data); + label_sha1_fingerprint_full->setText(QString::fromStdString(fingerprint_str)); + continue; + } - m_pLabelLocalFingerprint->setText(QString::fromStdString(formatted_fingerprint)); - m_pLabelLocalFingerprint->setTextInteractionFlags(Qt::TextSelectableByMouse); + if (fingerprint.algorithm == "sha256") { + auto fingerprint_str = barrier::format_ssl_fingerprint(fingerprint.data); + fingerprint_str.resize(40); + fingerprint_str += " ..."; + + auto fingerprint_str_cols = barrier::format_ssl_fingerprint_columns(fingerprint.data); + auto fingerprint_randomart = barrier::create_fingerprint_randomart(fingerprint.data); + + m_pLabelLocalFingerprint->setText(QString::fromStdString(fingerprint_str)); + label_sha256_fingerprint_full->setText(QString::fromStdString(fingerprint_str_cols)); + label_sha256_randomart->setText(QString::fromStdString(fingerprint_randomart)); + } + } + + toolbutton_show_fingerprint->setEnabled(true); } void MainWindow::on_m_pGroupClient_toggled(bool on) diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index 9a6c615d..59a0e0db 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -203,6 +203,8 @@ public slots: QStringList m_PendingClientNames; LogWindow *m_pLogWindow; + bool m_fingerprint_expanded = false; + private slots: void on_m_pCheckBoxAutoConfig_toggled(bool checked); void on_m_pComboServerList_currentIndexChanged(QString ); diff --git a/src/gui/src/MainWindowBase.ui b/src/gui/src/MainWindowBase.ui index 3e31ac9f..9f8d4896 100644 --- a/src/gui/src/MainWindowBase.ui +++ b/src/gui/src/MainWindowBase.ui @@ -75,10 +75,87 @@ + + Qt::PlainText + + + + + + + ... + + + Qt::DownArrow + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + QLayout::SetMinimumSize + + + + + + Courier + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + SHA1 (deprecated, compare to old clients only): + + + + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + SHA256: + + + + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + @@ -242,7 +319,7 @@ - :/res/icons/16x16/padlock.png + :/res/icons/16x16/padlock.png @@ -377,7 +454,7 @@ - Show &Log + Show &Log Show Log @@ -388,7 +465,7 @@ - + diff --git a/src/gui/src/SslCertificate.cpp b/src/gui/src/SslCertificate.cpp index 39c63674..ea770503 100644 --- a/src/gui/src/SslCertificate.cpp +++ b/src/gui/src/SslCertificate.cpp @@ -73,12 +73,12 @@ void SslCertificate::generateCertificate() void SslCertificate::generateFingerprint(const std::string& cert_path) { try { - auto fingerprint = barrier::get_pem_file_cert_fingerprint(cert_path, - barrier::FingerprintType::SHA1); - auto local_path = DataDirectories::local_ssl_fingerprints_path(); barrier::FingerprintDatabase db; - db.add_trusted(fingerprint); + db.add_trusted(barrier::get_pem_file_cert_fingerprint(cert_path, + barrier::FingerprintType::SHA1)); + db.add_trusted(barrier::get_pem_file_cert_fingerprint(cert_path, + barrier::FingerprintType::SHA256)); db.write(local_path); emit info(tr("SSL fingerprint generated.")); diff --git a/src/lib/net/SecureSocket.cpp b/src/lib/net/SecureSocket.cpp index 245f5287..3c65d9ac 100644 --- a/src/lib/net/SecureSocket.cpp +++ b/src/lib/net/SecureSocket.cpp @@ -657,17 +657,22 @@ bool SecureSocket::verifyCertFingerprint() { // calculate received certificate fingerprint - barrier::FingerprintData fingerprint; + barrier::FingerprintData fingerprint_sha1, fingerprint_sha256; try { - fingerprint = barrier::get_ssl_cert_fingerprint(SSL_get_peer_certificate(m_ssl->m_ssl), - barrier::FingerprintType::SHA1); + auto* cert = SSL_get_peer_certificate(m_ssl->m_ssl); + fingerprint_sha1 = barrier::get_ssl_cert_fingerprint(cert, + barrier::FingerprintType::SHA1); + fingerprint_sha256 = barrier::get_ssl_cert_fingerprint(cert, + barrier::FingerprintType::SHA256); } catch (const std::exception& e) { LOG((CLOG_ERR "%s", e.what())); return false; } - LOG((CLOG_NOTE "server fingerprint: %s", - barrier::format_ssl_fingerprint(fingerprint.data).c_str())); + // note: the GUI parses the following two lines of logs, don't change unnecessarily + LOG((CLOG_NOTE "server fingerprint (SHA1): %s (SHA256): %s", + barrier::format_ssl_fingerprint(fingerprint_sha1.data).c_str(), + barrier::format_ssl_fingerprint(fingerprint_sha256.data).c_str())); auto fingerprint_db_path = DataDirectories::trusted_servers_ssl_fingerprints_path(); @@ -685,7 +690,7 @@ SecureSocket::verifyCertFingerprint() fingerprint_db_path.c_str())); } - if (db.is_trusted(fingerprint)) { + if (db.is_trusted(fingerprint_sha256)) { LOG((CLOG_NOTE "Fingerprint matches trusted fingerprint")); return true; } else { diff --git a/src/lib/net/SecureUtils.cpp b/src/lib/net/SecureUtils.cpp index c7e0a82d..b99dd38c 100644 --- a/src/lib/net/SecureUtils.cpp +++ b/src/lib/net/SecureUtils.cpp @@ -89,6 +89,29 @@ std::string format_ssl_fingerprint(const std::vector& fingerprint, bool return result; } +std::string format_ssl_fingerprint_columns(const std::vector& fingerprint) +{ + auto max_columns = 8; + + std::string hex = barrier::string::to_hex(fingerprint, 2); + barrier::string::uppercase(hex); + if (hex.empty() || hex.size() % 2 != 0) { + return hex; + } + + std::string separated; + for (std::size_t i = 0; i < hex.size(); i += max_columns * 2) { + for (std::size_t j = i; j < i + 16 && j < hex.size() - 1; j += 2) { + separated.push_back(hex[j]); + separated.push_back(hex[j + 1]); + separated.push_back(':'); + } + separated.push_back('\n'); + } + separated.pop_back(); // we don't need last newline character + return separated; +} + FingerprintData get_ssl_cert_fingerprint(X509* cert, FingerprintType type) { if (!cert) { diff --git a/src/lib/net/SecureUtils.h b/src/lib/net/SecureUtils.h index 7525381f..c4d51f33 100644 --- a/src/lib/net/SecureUtils.h +++ b/src/lib/net/SecureUtils.h @@ -28,6 +28,7 @@ namespace barrier { std::string format_ssl_fingerprint(const std::vector& fingerprint, bool separator = true); +std::string format_ssl_fingerprint_columns(const std::vector& fingerprint); FingerprintData get_ssl_cert_fingerprint(X509* cert, FingerprintType type);