From e6e5bf832778ada0e9e4b1a478528b3cf6aff092 Mon Sep 17 00:00:00 2001 From: Tixx <83774803+WiserTixx@users.noreply.github.com> Date: Thu, 17 Jul 2025 14:32:17 +0200 Subject: [PATCH 1/4] Fix launcher update to delete backup after download --- src/Startup.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Startup.cpp b/src/Startup.cpp index 480566b..9ab0eae 100644 --- a/src/Startup.cpp +++ b/src/Startup.cpp @@ -179,14 +179,15 @@ void CheckForUpdates(const std::string& CV) { #if defined(__linux__) error("Auto update is NOT implemented for the Linux version. Please update manually ASAP as updates contain security patches."); #else - fs::remove(Back); - fs::rename(EP, Back); info("Downloading Launcher update " + LatestHash); HTTP::Download( "https://backend.beammp.com/builds/launcher?download=true" "&pk=" + PublicKey + "&branch=" + Branch, - EP); + beammp_wide("new_") + EP); + fs::remove(Back, ec); + fs::rename(EP, Back); + fs::rename(beammp_wide("new_") + EP, EP); URelaunch(); #endif } else { From c0ed056440ef4c1a94bc622a874d40df256b5b75 Mon Sep 17 00:00:00 2001 From: Tixx <83774803+WiserTixx@users.noreply.github.com> Date: Thu, 17 Jul 2025 14:33:25 +0200 Subject: [PATCH 2/4] Fix for backup failing when it's in use The backup file may still be in use by older launcher versions (v2.0.71) if they update --- src/Startup.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Startup.cpp b/src/Startup.cpp index 9ab0eae..518eee7 100644 --- a/src/Startup.cpp +++ b/src/Startup.cpp @@ -185,8 +185,14 @@ void CheckForUpdates(const std::string& CV) { "&pk=" + PublicKey + "&branch=" + Branch, beammp_wide("new_") + EP); + std::error_code ec; fs::remove(Back, ec); + if (ec == std::errc::permission_denied) { + error("Failed to remove old backup file: " + ec.message() + ". Using alternative name."); + fs::rename(EP, Back + beammp_wide(".") + Utils::ToWString(FileHash.substr(0, 8))); + } else { fs::rename(EP, Back); + } fs::rename(beammp_wide("new_") + EP, EP); URelaunch(); #endif From b9cc0250838a62d3813520475744216b9b3bd210 Mon Sep 17 00:00:00 2001 From: Tixx <83774803+WiserTixx@users.noreply.github.com> Date: Fri, 25 Jul 2025 23:50:16 +0200 Subject: [PATCH 3/4] Check hashes after downloading --- include/Http.h | 2 +- include/Utils.h | 49 ++++++++++++++++++++++++++++++++++++--- src/Network/Http.cpp | 12 +++++++++- src/Network/Resources.cpp | 6 ++--- src/Startup.cpp | 8 +++---- 5 files changed, 65 insertions(+), 12 deletions(-) diff --git a/include/Http.h b/include/Http.h index f491dc4..1e75590 100644 --- a/include/Http.h +++ b/include/Http.h @@ -11,7 +11,7 @@ #include class HTTP { public: - static bool Download(const std::string& IP, const beammp_fs_string& Path); + static bool Download(const std::string& IP, const beammp_fs_string& Path, const std::string& Hash); static std::string Post(const std::string& IP, const std::string& Fields); static std::string Get(const std::string& IP); static bool ProgressBar(size_t c, size_t t); diff --git a/include/Utils.h b/include/Utils.h index 755e170..00a8a64 100644 --- a/include/Utils.h +++ b/include/Utils.h @@ -29,7 +29,6 @@ #endif namespace Utils { - inline std::vector Split(const std::string& String, const std::string& delimiter) { std::vector Val; size_t pos; @@ -166,7 +165,7 @@ namespace Utils { } #ifdef _WIN32 -inline std::wstring ToWString(const std::string& s) { + inline std::wstring ToWString(const std::string& s) { if (s.empty()) return std::wstring(); int size_needed = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), (int)s.size(), nullptr, 0); @@ -185,7 +184,7 @@ inline std::wstring ToWString(const std::string& s) { return s; } #endif - inline std::string GetSha256HashReallyFast(const beammp_fs_string& filename) { + inline std::string GetSha256HashReallyFastFile(const beammp_fs_string& filename) { try { EVP_MD_CTX* mdctx; const EVP_MD* md; @@ -233,6 +232,50 @@ inline std::wstring ToWString(const std::string& s) { } EVP_MD_CTX_free(mdctx); + std::string result; + for (size_t i = 0; i < sha256_len; i++) { + char buf[3]; + sprintf(buf, "%02x", sha256_value[i]); + buf[2] = 0; + result += buf; + } + return result; + } catch (const std::exception& e) { + error(beammp_wide("Sha256 hashing of '") + filename + beammp_wide("' failed: ") + ToWString(e.what())); + return ""; + } + } + inline std::string GetSha256HashReallyFast(const std::string& text, const beammp_fs_string& filename) { + try { + EVP_MD_CTX* mdctx; + const EVP_MD* md; + uint8_t sha256_value[EVP_MAX_MD_SIZE]; + md = EVP_sha256(); + if (md == nullptr) { + throw std::runtime_error("EVP_sha256() failed"); + } + + mdctx = EVP_MD_CTX_new(); + if (mdctx == nullptr) { + throw std::runtime_error("EVP_MD_CTX_new() failed"); + } + if (!EVP_DigestInit_ex2(mdctx, md, NULL)) { + EVP_MD_CTX_free(mdctx); + throw std::runtime_error("EVP_DigestInit_ex2() failed"); + } + + if (!EVP_DigestUpdate(mdctx, text.data(), text.size())) { + EVP_MD_CTX_free(mdctx); + throw std::runtime_error("EVP_DigestUpdate() failed"); + } + + unsigned int sha256_len = 0; + if (!EVP_DigestFinal_ex(mdctx, sha256_value, &sha256_len)) { + EVP_MD_CTX_free(mdctx); + throw std::runtime_error("EVP_DigestFinal_ex() failed"); + } + EVP_MD_CTX_free(mdctx); + std::string result; for (size_t i = 0; i < sha256_len; i++) { char buf[3]; diff --git a/src/Network/Http.cpp b/src/Network/Http.cpp index 92d8500..42c4e37 100644 --- a/src/Network/Http.cpp +++ b/src/Network/Http.cpp @@ -127,7 +127,7 @@ std::string HTTP::Post(const std::string& IP, const std::string& Fields) { return Ret; } -bool HTTP::Download(const std::string& IP, const beammp_fs_string& Path) { +bool HTTP::Download(const std::string& IP, const beammp_fs_string& Path, const std::string& Hash) { static std::mutex Lock; std::scoped_lock Guard(Lock); @@ -139,6 +139,16 @@ bool HTTP::Download(const std::string& IP, const beammp_fs_string& Path) { return false; } + std::string RetHash = Utils::GetSha256HashReallyFast(Ret, Path); + + debug("Return hash: " + RetHash); + debug("Expected hash: " + Hash); + + if (RetHash != Hash) { + error("Downloaded file hash does not match expected hash"); + return false; + } + std::ofstream File(Path, std::ios::binary); if (File.is_open()) { File << Ret; diff --git a/src/Network/Resources.cpp b/src/Network/Resources.cpp index ea99d98..1696dde 100644 --- a/src/Network/Resources.cpp +++ b/src/Network/Resources.cpp @@ -445,7 +445,7 @@ void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vectorFileName).stem().string() + "-" + ModInfoIter->Hash.substr(0, 8) + std::filesystem::path(ModInfoIter->FileName).extension().string(); auto PathToSaveTo = (CachingDirectory / FileName); - if (fs::exists(PathToSaveTo) && Utils::GetSha256HashReallyFast(PathToSaveTo) == ModInfoIter->Hash) { + if (fs::exists(PathToSaveTo) && Utils::GetSha256HashReallyFastFile(PathToSaveTo) == ModInfoIter->Hash) { debug("Mod '" + FileName + "' found in cache"); UpdateUl(false, std::to_string(ModNo) + "/" + std::to_string(TotalMods) + ": " + ModInfoIter->FileName); std::this_thread::sleep_for(std::chrono::milliseconds(50)); @@ -476,7 +476,7 @@ void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vectorFileName).filename(); - fs::exists(OldCachedPath) && Utils::GetSha256HashReallyFast(OldCachedPath) == ModInfoIter->Hash) { + fs::exists(OldCachedPath) && Utils::GetSha256HashReallyFastFile(OldCachedPath) == ModInfoIter->Hash) { debug("Mod '" + FileName + "' found in old cache, copying it to the new cache"); UpdateUl(false, std::to_string(ModNo) + "/" + std::to_string(TotalMods) + ": " + ModInfoIter->FileName); std::this_thread::sleep_for(std::chrono::milliseconds(50)); @@ -562,7 +562,7 @@ void NewSyncResources(SOCKET Sock, const std::string& Mods, const std::vectorHash) { + if (Utils::GetSha256HashReallyFastFile(PathToSaveTo) != ModInfoIter->Hash) { error(beammp_wide("Failed to write or download the entire file '") + beammp_fs_string(PathToSaveTo) + beammp_wide("' correctly (hash mismatch)")); Terminate = true; } diff --git a/src/Startup.cpp b/src/Startup.cpp index 518eee7..0335877 100644 --- a/src/Startup.cpp +++ b/src/Startup.cpp @@ -171,7 +171,7 @@ void CheckForUpdates(const std::string& CV) { transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower); beammp_fs_string EP(GetEP() + GetEN()), Back(GetEP() + beammp_wide("BeamMP-Launcher.back")); - std::string FileHash = Utils::GetSha256HashReallyFast(EP); + std::string FileHash = Utils::GetSha256HashReallyFastFile(EP); if (FileHash != LatestHash && IsOutdated(Version(VersionStrToInts(GetVer() + GetPatch())), Version(VersionStrToInts(LatestVersion)))) { if (!options.no_update) { @@ -184,7 +184,7 @@ void CheckForUpdates(const std::string& CV) { "https://backend.beammp.com/builds/launcher?download=true" "&pk=" + PublicKey + "&branch=" + Branch, - beammp_wide("new_") + EP); + beammp_wide("new_") + EP, LatestHash); std::error_code ec; fs::remove(Back, ec); if (ec == std::errc::permission_denied) { @@ -336,14 +336,14 @@ void PreGame(const beammp_fs_string& GamePath) { std::string ZipPath(GetGamePath() / R"(mods/multiplayer/beammp.zip)"); #endif - std::string FileHash = fs::exists(ZipPath) ? Utils::GetSha256HashReallyFast(ZipPath) : ""; + std::string FileHash = fs::exists(ZipPath) ? Utils::GetSha256HashReallyFastFile(ZipPath) : ""; if (FileHash != LatestHash) { info("Downloading BeamMP Update " + LatestHash); HTTP::Download("https://backend.beammp.com/builds/client?download=true" "&pk=" + PublicKey + "&branch=" + Branch, - ZipPath); + ZipPath, LatestHash); } beammp_fs_string Target(GetGamePath() / beammp_wide("mods/unpacked/beammp")); From a87aa7230a2745db8d6a6d807e87a28f3fa6d412 Mon Sep 17 00:00:00 2001 From: Tixx <83774803+WiserTixx@users.noreply.github.com> Date: Sun, 27 Jul 2025 01:07:23 +0200 Subject: [PATCH 4/4] Make null termination in hex encoding more clear --- include/Utils.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/Utils.h b/include/Utils.h index 00a8a64..3c8381e 100644 --- a/include/Utils.h +++ b/include/Utils.h @@ -236,7 +236,7 @@ namespace Utils { for (size_t i = 0; i < sha256_len; i++) { char buf[3]; sprintf(buf, "%02x", sha256_value[i]); - buf[2] = 0; + buf[2] = NULL; result += buf; } return result; @@ -280,7 +280,7 @@ namespace Utils { for (size_t i = 0; i < sha256_len; i++) { char buf[3]; sprintf(buf, "%02x", sha256_value[i]); - buf[2] = 0; + buf[2] = NULL; result += buf; } return result;