From 743b6697364e4d8cb0b862c5ca94e67feea3e776 Mon Sep 17 00:00:00 2001 From: Don Ho Date: Thu, 22 Jan 2026 17:29:26 +0100 Subject: [PATCH] Launch only program signed with WinGUp's certificate on plugin management While using -clean or -unzip for plugin management, WinGUp retrieves its own signing certificate, and compare it with the program to be launched. If the certificates do not match, or one of bineries is not signed, the program is not launched. --- src/verifySignedfile.cpp | 71 ++++++++++++++++++++++++---------------- src/verifySignedfile.h | 5 ++- src/winmain.cpp | 46 ++++++++++++++++++++++++-- 3 files changed, 91 insertions(+), 31 deletions(-) diff --git a/src/verifySignedfile.cpp b/src/verifySignedfile.cpp index 56c2d66a..8b203024 100644 --- a/src/verifySignedfile.cpp +++ b/src/verifySignedfile.cpp @@ -62,22 +62,24 @@ void SecurityGuard::writeSecurityError(const std::wstring& prefix, const std::ws writeLog(expandedLogFileName.c_str(), prefix.c_str(), log2write.c_str()); } -bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) +bool SecurityGuard::initFromSelfCertif() { - wstring display_name; - wstring key_id_hex; - wstring subject; - wstring authority_key_id_hex; + wchar_t codeSigedBinPath[MAX_PATH]{}; + ::GetModuleFileName(NULL, codeSigedBinPath, MAX_PATH); + + return verifySignatureAndGetInfo(codeSigedBinPath, _signer_display_name, _signer_key_id, _signer_subject, _authority_key_id); +} +bool SecurityGuard::verifySignatureAndGetInfo(const std::wstring& codeSigedBinPath, wstring& display_name, wstring& key_id_hex, wstring& subject, wstring& authority_key_id_hex) +{ // // Signature verification // // Initialize the WINTRUST_FILE_INFO structure. - LPCWSTR pwszfilepath = filepath.c_str(); WINTRUST_FILE_INFO file_data = {}; file_data.cbStruct = sizeof(WINTRUST_FILE_INFO); - file_data.pcwszFilePath = pwszfilepath; + file_data.pcwszFilePath = codeSigedBinPath.c_str(); // Initialise WinTrust data WINTRUST_DATA winTEXTrust_data = {}; @@ -121,13 +123,13 @@ bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) if (vtrust) { - writeSecurityError(filepath.c_str(), L": chain of trust verification failed"); + writeSecurityError(codeSigedBinPath, L": chain of trust verification failed"); return false; } if (t2) { - writeSecurityError(filepath.c_str(), L": error encountered while cleaning up after WinVerifyTrust"); + writeSecurityError(codeSigedBinPath, L": error encountered while cleaning up after WinVerifyTrust"); return false; } } @@ -143,14 +145,14 @@ bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) bool status = true; try { - BOOL result = ::CryptQueryObject(CERT_QUERY_OBJECT_FILE, filepath.c_str(), + BOOL result = ::CryptQueryObject(CERT_QUERY_OBJECT_FILE, codeSigedBinPath.c_str(), CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, CERT_QUERY_FORMAT_FLAG_BINARY, 0, &dwEncoding, &dwContentType, &dwFormatType, &hStore, &hMsg, NULL); if (!result) { - throw string("Checking certificate of ") + ws2s(filepath) + " : " + ws2s(GetLastErrorAsString(GetLastError())); + throw string("Checking certificate of ") + ws2s(codeSigedBinPath) + " : " + ws2s(GetLastErrorAsString(GetLastError())); } // Get signer information size. @@ -235,13 +237,12 @@ bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) // --- Retrieve Authority Key Identifier (AKI) --- - - PCERT_EXTENSION pExtension = ::CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER2, // OID for Authority Key Identifier (2.5.29.35) - context->pCertInfo->cExtension, context->pCertInfo->rgExtension); + // OID for Authority Key Identifier (2.5.29.35) + PCERT_EXTENSION pExtension = ::CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER2, context->pCertInfo->cExtension, context->pCertInfo->rgExtension); if (!pExtension) - pExtension = ::CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER, // OID for Authority Key Identifier (2.5.29.1) - context->pCertInfo->cExtension, context->pCertInfo->rgExtension); + // OID for Authority Key Identifier (2.5.29.1) + pExtension = ::CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER, context->pCertInfo->cExtension, context->pCertInfo->rgExtension); if (pExtension) { @@ -278,15 +279,35 @@ bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) } catch (const string& s) { - writeSecurityError((filepath + L" - error while getting certificate information: ").c_str(), s2ws(s).c_str()); + wstring msg = codeSigedBinPath; + msg += L" - error while getting certificate information: "; + writeSecurityError(msg, s2ws(s)); status = false; } catch (...) { // Unknown error - writeSecurityError(filepath.c_str(), L": Unknow error while getting certificate information"); + writeSecurityError(codeSigedBinPath, L": Unknow error while getting certificate information"); status = false; } + // Clean up. + + if (hStore != NULL) CertCloseStore(hStore, 0); + if (hMsg != NULL) CryptMsgClose(hMsg); + if (pSignerInfo != NULL) LocalFree(pSignerInfo); + + return status; +} + +bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) +{ + wstring display_name; + wstring key_id_hex; + wstring subject; + wstring authority_key_id_hex; + + bool status = verifySignatureAndGetInfo(filepath, display_name, key_id_hex, subject, authority_key_id_hex); + // // fields verifications - if status is true, and demaded parameter string to compare (from the parameter) is not empty, then do compare // @@ -298,7 +319,7 @@ bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) errMsg += _signer_display_name; errMsg += L" vs unexpected "; errMsg += display_name; - writeSecurityError(filepath.c_str(), errMsg); + writeSecurityError(filepath, errMsg); } if (status && (!_signer_subject.empty() && _signer_subject != subject)) @@ -309,7 +330,7 @@ bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) errMsg += _signer_subject; errMsg += L" vs unexpected "; errMsg += subject; - writeSecurityError(filepath.c_str(), errMsg); + writeSecurityError(filepath, errMsg); } if (status && (!_signer_key_id.empty() && stringToUpper(_signer_key_id) != key_id_hex)) @@ -320,7 +341,7 @@ bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) errMsg += _signer_key_id; errMsg += L" vs unexpected "; errMsg += key_id_hex; - writeSecurityError(filepath.c_str(), errMsg); + writeSecurityError(filepath, errMsg); } if (status && (!_authority_key_id.empty() && stringToUpper(_authority_key_id) != authority_key_id_hex)) @@ -331,14 +352,8 @@ bool SecurityGuard::verifySignedBinary(const std::wstring& filepath) errMsg += _authority_key_id; errMsg += L" vs unexpected "; errMsg += authority_key_id_hex; - writeSecurityError(filepath.c_str(), errMsg); + writeSecurityError(filepath, errMsg); } - // Clean up. - - if (hStore != NULL) CertCloseStore(hStore, 0); - if (hMsg != NULL) CryptMsgClose(hMsg); - if (pSignerInfo != NULL) LocalFree(pSignerInfo); - return status; } diff --git a/src/verifySignedfile.h b/src/verifySignedfile.h index 3b7dc33b..9f09d491 100644 --- a/src/verifySignedfile.h +++ b/src/verifySignedfile.h @@ -53,8 +53,11 @@ class SecurityGuard final { public: SecurityGuard(){}; - bool verifySignedBinary(const std::wstring& filepath); + bool initFromSelfCertif(); + bool verifySignatureAndGetInfo(const std::wstring& codeSigedBinPath, std::wstring& display_name, std::wstring& key_id_hex, std::wstring& subject, std::wstring& authority_key_id_hex); + bool verifySignedBinary(const std::wstring& filepath); + void enableChkRevoc() { _doCheckRevocation = true; } void enableChkTrustChain() { _doCheckChainOfTrust = true; } void setDisplayName(const std::wstring& signer_display_name) { _signer_display_name = signer_display_name; } diff --git a/src/winmain.cpp b/src/winmain.cpp index 71fa17f4..7a1a36f9 100644 --- a/src/winmain.cpp +++ b/src/winmain.cpp @@ -1461,7 +1461,28 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR lpszCmdLine, int) deleteFileOrFolder(destPath); } - ::ShellExecute(NULL, L"open", L"explorer.exe", prog2Launch.c_str(), prog2LaunchDir, SW_SHOWNORMAL); + + +#ifdef _DEBUG + // Don't check any thing in debug mode +#else + // Check signature of the launched program, with the same certif as gup.exe + SecurityGuard securityGuard4PluginsInstall; + + if (!securityGuard4PluginsInstall.initFromSelfCertif()) + { + securityGuard.writeSecurityError(L"Above certificate init error from \"gup -clean\" (remove plugins)", L""); + return -1; + } + + bool isSecured = securityGuard4PluginsInstall.verifySignedBinary(prog2Launch.c_str()); + if (!isSecured) + { + securityGuard.writeSecurityError(L"Above certificate verification error from \"gup -clean\" (remove plugins)", L""); + return -1; + } +#endif + ::ShellExecute(NULL, L"open", prog2Launch.c_str(), NULL, prog2LaunchDir, SW_SHOWNORMAL); return 0; } @@ -1480,12 +1501,32 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR lpszCmdLine, int) WRITE_LOG(GUP_LOG_FILENAME, L"-1 in plugin updater's part - if (isCleanUp && isUnzip) // update: ", L"nbParam < 3"); return -1; } + wstring prog2Launch = params[0]; wchar_t prog2LaunchDir[MAX_PATH]; lstrcpy(prog2LaunchDir, prog2Launch.c_str()); ::PathRemoveFileSpec(prog2LaunchDir); wstring destPathRoot = params[1]; +#ifdef _DEBUG + // Don't check any thing in debug mode +#else + // Check signature of the launched program, with the same certif as gup.exe + SecurityGuard securityGuard4PluginsInstall; + + if (!securityGuard4PluginsInstall.initFromSelfCertif()) + { + securityGuard.writeSecurityError(L"Above certificate init error from \"gup -unzip\" (install or update plugins)", L""); + return -1; + } + + bool isSecured = securityGuard4PluginsInstall.verifySignedBinary(prog2Launch.c_str()); + if (!isSecured) + { + securityGuard.writeSecurityError(L"Above certificate verification error from \"gup -unzip\" (install or update plugins)", L""); + return -1; + } +#endif for (size_t i = 2; i < nbParam; ++i) { wstring destPath = destPathRoot; @@ -1593,7 +1634,8 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR lpszCmdLine, int) } } - ::ShellExecute(NULL, L"open", L"explorer.exe", prog2Launch.c_str(), prog2LaunchDir, SW_SHOWNORMAL); + ::ShellExecute(NULL, L"open", prog2Launch.c_str(), NULL, prog2LaunchDir, SW_SHOWNORMAL); + return 0; }