diff --git a/src/OrcCommand/Command/NTFSInfo/NTFSInfo.h b/src/OrcCommand/Command/NTFSInfo/NTFSInfo.h index 7f66e2d6..03c66d4e 100644 --- a/src/OrcCommand/Command/NTFSInfo/NTFSInfo.h +++ b/src/OrcCommand/Command/NTFSInfo/NTFSInfo.h @@ -115,6 +115,7 @@ class ORCUTILS_API Main : public UtilitiesMain DWORD dwTotalFileTreated; DWORD m_dwProgress; + std::shared_ptr m_authenticodeCache; Authenticode m_codeVerifier; HRESULT Prepare(); @@ -213,7 +214,11 @@ class ORCUTILS_API Main : public UtilitiesMain , m_TimeLineOutput() , m_AttrOutput() , m_I30Output() - , m_SecDescrOutput() {}; + , m_SecDescrOutput() + , m_authenticodeCache(std::make_shared()) + { + m_codeVerifier.SetCache(m_authenticodeCache); + } // implemented in NTFSInfo_Output.cpp void PrintUsage(); diff --git a/src/OrcLib/Authenticode.cpp b/src/OrcLib/Authenticode.cpp index fdd8604a..bf653c54 100644 --- a/src/OrcLib/Authenticode.cpp +++ b/src/OrcLib/Authenticode.cpp @@ -201,6 +201,115 @@ Authenticode::PE_Hashs To_PE_Hashs(const PeParser::PeHash& hashes) return h; } +void GetSignersName(const std::vector& signers, std::vector& names) +{ + for (const auto& signer : signers) + { + std::wstring name; + const auto requiredLength = CertGetNameStringW(signer, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0L, NULL, NULL, 0); + name.resize(requiredLength); + + CertGetNameStringW(signer, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0L, NULL, name.data(), name.size()); + name.resize(requiredLength - 1); // remove trailing '\0' + + names.push_back(std::move(name)); + } +} + +void GetSignersCertificateAuthoritiesName( + const std::vector& certificateAuthorities, + std::vector& names) +{ + for (const auto& ca : certificateAuthorities) + { + std::wstring name; + const auto requiredLength = CertGetNameStringW(ca, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0L, NULL, NULL, 0); + name.resize(requiredLength); + + CertGetNameStringW(ca, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0L, NULL, name.data(), name.size()); + name.resize(requiredLength - 1); // remove trailing '\0' + + names.push_back(std::move(name)); + } +} + +HRESULT +GetSignersThumbprint(const std::vector& signers, std::vector& thumbprints) +{ + for (const auto& signer : signers) + { + std::vector thumbprint; + thumbprint.resize(BYTES_IN_SHA256_HASH); + + DWORD thumbprintSize = thumbprint.size(); + if (!CertGetCertificateContextProperty(signer, CERT_HASH_PROP_ID, thumbprint.data(), &thumbprintSize)) + { + auto lastError = GetLastError(); + Log::Debug("Failed to extract signer thumbprint [{}]", Win32Error(lastError)); + return HRESULT_FROM_WIN32(lastError); + } + + thumbprint.resize(thumbprintSize); + thumbprints.push_back(std::move(thumbprint)); + } + + return S_OK; +} + +HRESULT GetSignersCertificateAuthoritiesThumbprint( + const std::vector& signers, + std::vector& thumbprints) +{ + for (const auto& signer : signers) + { + std::vector thumbprint; + thumbprint.resize(BYTES_IN_SHA256_HASH); + + DWORD thumbprintSize = thumbprint.size(); + if (!CertGetCertificateContextProperty(signer, CERT_HASH_PROP_ID, thumbprint.data(), &thumbprintSize)) + { + auto lastError = GetLastError(); + Log::Debug("Failed to extract signers certificate authorities thumbprint [{}]", Win32Error(lastError)); + return HRESULT_FROM_WIN32(lastError); + } + + thumbprint.resize(thumbprintSize); + thumbprints.push_back(std::move(thumbprint)); + } + + return S_OK; +} + +const std::shared_ptr& UpdateSignersCache( + AuthenticodeCache& cache, + std::wstring_view catalogPath, + std::vector& signers, + std::vector& signersCertificateAuthorities) +{ + auto info = std::make_shared(); + info->catalogPath = catalogPath; + + GetSignersName(signers, info->names); + GetSignersCertificateAuthoritiesName(signersCertificateAuthorities, info->certificateAuthoritiesName); + + HRESULT hr = GetSignersThumbprint(signers, info->thumbprints); + if (FAILED(hr)) + { + Log::Error("Failed to extract signers thumprint [{}]", SystemError(hr)); + } + + hr = GetSignersCertificateAuthoritiesThumbprint( + signersCertificateAuthorities, info->certificateAuthoritiesThumbprint); + if (FAILED(hr)) + { + Log::Error("Failed to extract signers certificate authorities thumprint [{}]", SystemError(hr)); + } + + auto signersInfo = std::make_shared(); + + return cache.Update(catalogPath, std::move(info)); +} + } // namespace static GUID WVTPolicyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2; @@ -263,7 +372,10 @@ DWORD Authenticode::ExpectedHashSize() szPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0L, NULL); if (hFile == INVALID_HANDLE_VALUE) return (DWORD)-1; - BOOST_SCOPE_EXIT(hFile) { CloseHandle(hFile); } + BOOST_SCOPE_EXIT(hFile) + { + CloseHandle(hFile); + } BOOST_SCOPE_EXIT_END; HCATADMIN hContext = NULL; @@ -272,7 +384,10 @@ DWORD Authenticode::ExpectedHashSize() { return (DWORD)-1; } - BOOST_SCOPE_EXIT(hContext) { CryptCATAdminReleaseContext(hContext, 0); } + BOOST_SCOPE_EXIT(hContext) + { + CryptCATAdminReleaseContext(hContext, 0); + } BOOST_SCOPE_EXIT_END; DWORD cbHash = 0L; @@ -395,13 +510,28 @@ Authenticode::VerifySignatureWithCatalogs( WintrustStructure.dwProvFlags = WTD_REVOCATION_CHECK_NONE; WintrustStructure.dwUIContext = 0L; - // WinVerifyTrust verifies signatures as specified by the GUID - // and Wintrust_Data. + // WinVerifyTrust verifies signatures as specified by the GUID and Wintrust_Data. LONG lStatus = m_wintrust.WinVerifyTrust((HWND)INVALID_HANDLE_VALUE, &WVTPolicyGUID, &WintrustStructure); - if (FAILED(ExtractCatalogSigners(InfoStruct.wszCatalogFile, data.Signers, data.SignersCAs, data.CertStores))) + // TODO: why here ? why not lazy ? CertStores ? { - Log::Debug(L"Failed to extract signer information from catalog '{}'", InfoStruct.wszCatalogFile); + fmt::basic_memory_buffer catalogData; + auto rv = ::MapFile(InfoStruct.wszCatalogFile, catalogData); + if (rv.has_error()) + { + Log::Error( + L"Failed to extract signature information from catalog '{}' [{}]", + InfoStruct.wszCatalogFile, + rv.error()); + return ToHRESULT(rv.error()); + } + + HRESULT hr = ExtractCatalogSigners( + InfoStruct.wszCatalogFile, std::string_view(catalogData.data(), catalogData.size()), data); + if (FAILED(hr)) + { + Log::Debug(L"Failed to extract signer information from catalog '{}'", InfoStruct.wszCatalogFile); + } } if (FAILED(EvaluateCheck(lStatus, data))) @@ -692,7 +822,10 @@ HRESULT Orc::Authenticode::ExtractSignatureSize(const CBinaryBuffer& signature, Log::Debug("Failed CryptQueryObject [{}]", SystemError(hr)); return hr; } - BOOST_SCOPE_EXIT((&hMsg)) { CryptMsgClose(hMsg); } + BOOST_SCOPE_EXIT((&hMsg)) + { + CryptMsgClose(hMsg); + } BOOST_SCOPE_EXIT_END; if (dwContentType == CERT_QUERY_CONTENT_PKCS7_SIGNED && dwFormatType == CERT_QUERY_FORMAT_BINARY) @@ -740,7 +873,10 @@ HRESULT Authenticode::ExtractSignatureHash(const CBinaryBuffer& signature, Authe Log::Debug("Failed CryptQueryObject [{}]", SystemError(hr)); return hr; } - BOOST_SCOPE_EXIT((&hMsg)) { CryptMsgClose(hMsg); } + BOOST_SCOPE_EXIT((&hMsg)) + { + CryptMsgClose(hMsg); + } BOOST_SCOPE_EXIT_END; if (dwContentType == CERT_QUERY_CONTENT_PKCS7_SIGNED && dwFormatType == CERT_QUERY_FORMAT_BINARY) @@ -851,7 +987,10 @@ HRESULT Authenticode::ExtractSignatureTimeStamp(const CBinaryBuffer& signature, Log::Debug("Failed CryptQueryObject [{}]", SystemError(hr)); return hr; } - BOOST_SCOPE_EXIT((&hMsg)) { CryptMsgClose(hMsg); } + BOOST_SCOPE_EXIT((&hMsg)) + { + CryptMsgClose(hMsg); + } BOOST_SCOPE_EXIT_END; if (dwContentType == CERT_QUERY_CONTENT_PKCS7_SIGNED && dwFormatType == CERT_QUERY_FORMAT_BINARY) @@ -1022,7 +1161,10 @@ HRESULT Authenticode::ExtractNestedSignature( Log::Error("Failed CryptDecodeObject [{}]", SystemError(hr)); return hr; } - BOOST_SCOPE_EXIT((&hMsg)) { CryptMsgClose(hMsg); } + BOOST_SCOPE_EXIT((&hMsg)) + { + CryptMsgClose(hMsg); + } BOOST_SCOPE_EXIT_END; certStores.push_back(hCertStore); @@ -1089,7 +1231,10 @@ HRESULT Authenticode::ExtractSignatureSigners( certStores.push_back(hCertStore); - BOOST_SCOPE_EXIT((&hMsg)) { CryptMsgClose(hMsg); } + BOOST_SCOPE_EXIT((&hMsg)) + { + CryptMsgClose(hMsg); + } BOOST_SCOPE_EXIT_END; PCCERT_CONTEXT pSigner = NULL; @@ -1141,7 +1286,10 @@ HRESULT Authenticode::ExtractSignatureSigners( Log::Debug("Failed to obtain certificate chain [{}]", SystemError(hr)); break; } - BOOST_SCOPE_EXIT(pChain) { CertFreeCertificateChain(pChain); } + BOOST_SCOPE_EXIT(pChain) + { + CertFreeCertificateChain(pChain); + } BOOST_SCOPE_EXIT_END; // Go through each simple chain to obtain all root CAs @@ -1158,8 +1306,43 @@ HRESULT Authenticode::ExtractSignatureSigners( return S_OK; } +HRESULT +Authenticode::ExtractCatalogSigners(std::wstring_view catalogPath, std::string_view catalogData, AuthenticodeData& data) +{ + if (data.SignersInfo()) + { + return S_OK; + } + + if (data.AuthenticodeCache()) + { + auto info = data.AuthenticodeCache()->Find(catalogPath); + if (info) + { + data.SetSignerInfo(std::move(info)); + return S_OK; + } + } + + HRESULT hr = ExtractCatalogSigners(catalogData, data.Signers(), data.SignersCAs(), data.CertStores); + if (FAILED(hr)) + { + Log::Debug(L"Failed to extract signer information from catalog '{}'", catalogPath); + return hr; + } + + if (data.AuthenticodeCache()) + { + auto signersInfo = + ::UpdateSignersCache(*data.AuthenticodeCache(), catalogPath, data.Signers(), data.SignersCAs()); + data.SetSignerInfo(std::move(signersInfo)); + } + + return S_OK; +} + HRESULT Authenticode::ExtractCatalogSigners( - std::string_view catalog, + std::string_view catalogData, std::vector& pSigners, std::vector& pCAs, std::vector& certStores) @@ -1172,8 +1355,8 @@ HRESULT Authenticode::ExtractCatalogSigners( { CRYPT_DATA_BLOB blob; - blob.cbData = catalog.size(); - blob.pbData = reinterpret_cast(const_cast(catalog.data())); + blob.cbData = catalogData.size(); + blob.pbData = reinterpret_cast(const_cast(catalogData.data())); if (!CryptQueryObject( CERT_QUERY_OBJECT_BLOB, @@ -1193,7 +1376,10 @@ HRESULT Authenticode::ExtractCatalogSigners( } } - BOOST_SCOPE_EXIT((&hMsg)) { CryptMsgClose(hMsg); } + BOOST_SCOPE_EXIT((&hMsg)) + { + CryptMsgClose(hMsg); + } BOOST_SCOPE_EXIT_END; certStores.push_back(hCertStore); @@ -1234,7 +1420,10 @@ HRESULT Authenticode::ExtractCatalogSigners( Log::Error("Failed to obtain certificate chain [{}]", SystemError(hr)); return hr; } - BOOST_SCOPE_EXIT(pChain) { CertFreeCertificateChain(pChain); } + BOOST_SCOPE_EXIT(pChain) + { + CertFreeCertificateChain(pChain); + } BOOST_SCOPE_EXIT_END; // Go through each simple chain to obtain all root CAs @@ -1252,23 +1441,6 @@ HRESULT Authenticode::ExtractCatalogSigners( return S_OK; } -HRESULT Authenticode::ExtractCatalogSigners( - LPCWSTR szCatalogFile, - std::vector& pSigners, - std::vector& pCAs, - std::vector& certStores) -{ - fmt::basic_memory_buffer catalog; - auto rv = ::MapFile(szCatalogFile, catalog); - if (rv.has_error()) - { - Log::Error(L"Failed to extract signature information from catalog '{}' [{}]", szCatalogFile, rv.error()); - return ToHRESULT(rv.error()); - } - - return ExtractCatalogSigners(std::string_view(catalog.data(), catalog.size()), pSigners, pCAs, certStores); -} - HRESULT Authenticode::Verify(LPCWSTR szFileName, const CBinaryBuffer& secdir, const PE_Hashs& hashs, AuthenticodeData& data) { @@ -1352,7 +1524,7 @@ Authenticode::Verify(LPCWSTR szFileName, const CBinaryBuffer& secdir, const PE_H SystemError(hr)); } - hr = ExtractSignatureSigners(signature, data.Timestamp, data.Signers, data.SignersCAs, data.CertStores); + hr = ExtractSignatureSigners(signature, data.Timestamp, data.Signers(), data.SignersCAs(), data.CertStores); if (FAILED(hr)) { Log::Error( @@ -1434,6 +1606,74 @@ Authenticode::~Authenticode() } } +const std::vector& Authenticode::AuthenticodeData::SignersName() +{ + if (m_signersInfo) + { + return m_signersInfo->names; + } + + if (!m_signersName) + { + std::vector names; + GetSignersName(m_signers, names); + m_signersName = std::move(names); + } + + return *m_signersName; +} + +const std::vector& Authenticode::AuthenticodeData::SignersThumbprint() +{ + if (m_signersInfo) + { + return m_signersInfo->thumbprints; + } + + if (!m_signersThumbprint) + { + std::vector thumbprint; + GetSignersThumbprint(m_signers, thumbprint); + m_signersThumbprint = std::move(thumbprint); + } + + return *m_signersThumbprint; +} + +const std::vector& Authenticode::AuthenticodeData::SignersCertificateAuthoritiesName() +{ + if (m_signersInfo) + { + return m_signersInfo->certificateAuthoritiesName; + } + + if (!m_signersCertificateAuthoritiesName) + { + std::vector names; + GetSignersName(m_signersCertificateAuthorities, names); + m_signersCertificateAuthoritiesName = std::move(names); + } + + return *m_signersCertificateAuthoritiesName; +} + +const std::vector& Authenticode::AuthenticodeData::SignersCertificateAuthoritiesThumbprint() +{ + if (m_signersInfo) + { + return m_signersInfo->certificateAuthoritiesThumbprint; + } + + if (!m_signersCertificateAuthoritiesThumbprint) + { + std::vector thumbprints; + GetSignersThumbprint(m_signersCertificateAuthorities, thumbprints); + m_signersCertificateAuthoritiesThumbprint = std::move(thumbprints); + } + + return *m_signersCertificateAuthoritiesThumbprint; +} + Orc::Result Authenticode::VerifySignatureWithCatalogHint( std::string_view catalogHint, const Orc::Authenticode::PE_Hashs& peHashes, @@ -1457,6 +1697,7 @@ Orc::Result Authenticode::VerifySignatureWithCatalogHint( L"%WINDIR%\\system32\\CatRoot\\{{F750E6C3-38EE-11D1-85E5-00C04FC295EE}}\\{}"sv, L"%WINDIR%\\system32\\CatRoot\\{}"sv}; + auto& cache = data.AuthenticodeCache(); for (auto& catroot : catrootDirectories) { std::error_code ec; @@ -1473,8 +1714,7 @@ Orc::Result Authenticode::VerifySignatureWithCatalogHint( return rv.error(); } - HRESULT hr = Authenticode::ExtractCatalogSigners( - std::string_view(catalog.data(), catalog.size()), data.Signers, data.SignersCAs, data.CertStores); + HRESULT hr = ExtractCatalogSigners(catalogPath, std::string_view(catalog.data(), catalog.size()), data); if (FAILED(hr)) { return SystemError(hr); diff --git a/src/OrcLib/Authenticode.h b/src/OrcLib/Authenticode.h index 22ac3ff0..d5b707cf 100644 --- a/src/OrcLib/Authenticode.h +++ b/src/OrcLib/Authenticode.h @@ -21,6 +21,7 @@ #include "BinaryBuffer.h" #include +#include #pragma managed(push, off) @@ -29,9 +30,56 @@ namespace Orc { class CBinaryBuffer; class ByteStream; +class AuthenticodeCache +{ +public: + struct SignersInfo + { + using Thumbprint = std::vector; + + std::wstring_view catalogPath; + std::vector names; + std::vector thumbprints; + std::vector certificateAuthoritiesName; + std::vector certificateAuthoritiesThumbprint; + }; + + using CatalogPath = std::wstring_view; + using SignersCache = std::unordered_map>; + + std::shared_ptr Find(std::wstring_view catalogPath) + { + auto it = m_signers.find(catalogPath); + if (it != std::end(m_signers)) + { + return it->second; + } + + return nullptr; + } + + const std::shared_ptr& Update(std::wstring_view catalogPath, std::shared_ptr signersInfo) + { + // Required workaround for lifetime until until heterogeneous lookup in unordered map are available (using + // std::string_view key for a std::string key map) + m_catalogsPath.emplace_back(catalogPath); + + return m_signers[m_catalogsPath.back()] = std::move(signersInfo); + } + + SignersCache& Signers() { return m_signers; } + const SignersCache& Signers() const { return m_signers; } + +private: + std::vector m_catalogsPath; + SignersCache m_signers; +}; + class Authenticode { public: + using Thumbprint = AuthenticodeCache::SignersInfo::Thumbprint; + typedef enum { AUTHENTICODE_UNKNOWN = 0, @@ -54,36 +102,34 @@ class Authenticode class AuthenticodeData { public: - bool isSigned = false; - bool bSignatureVerifies = false; - Authenticode::Status AuthStatus = AUTHENTICODE_UNKNOWN; - std::vector Signers; - std::vector SignersCAs; - std::vector CertStores; - PE_Hashs SignedHashs; - FILETIME Timestamp {0}; - - AuthenticodeData() = default; + AuthenticodeData() {} + AuthenticodeData(std::shared_ptr cache) + : m_authenticodeCache(std::move(cache)) {}; AuthenticodeData(const AuthenticodeData& other) = default; AuthenticodeData(AuthenticodeData&& other) { std::swap(isSigned, other.isSigned); std::swap(bSignatureVerifies, other.bSignatureVerifies); - std::swap(Signers, other.Signers); - std::swap(SignersCAs, other.SignersCAs); + std::swap(m_signers, other.m_signers); + std::swap(m_signersName, other.m_signersName); + std::swap(m_signersCertificateAuthoritiesThumbprint, other.m_signersCertificateAuthoritiesThumbprint); + std::swap(m_signersCertificateAuthorities, other.m_signersCertificateAuthorities); + std::swap(m_signersCertificateAuthoritiesName, other.m_signersCertificateAuthoritiesName); + std::swap(m_signersCertificateAuthoritiesThumbprint, other.m_signersCertificateAuthoritiesThumbprint); + std::swap(m_signersInfo, other.m_signersInfo); } ~AuthenticodeData() { isSigned = false; bSignatureVerifies = false; - for (auto signer : Signers) + for (auto signer : m_signers) { CertFreeCertificateContext(signer); } - for (auto CA : SignersCAs) + for (auto CA : m_signersCertificateAuthorities) { CertFreeCertificateContext(CA); } @@ -93,20 +139,86 @@ class Authenticode } } + const std::vector& SignersName(); + const std::vector& SignersThumbprint(); + + const std::vector& SignersCertificateAuthoritiesName(); + const std::vector& SignersCertificateAuthoritiesThumbprint(); + + const std::shared_ptr& AuthenticodeCache() const { return m_authenticodeCache; } + std::shared_ptr& AuthenticodeCache() { return m_authenticodeCache; } + AuthenticodeData& operator=(const AuthenticodeData& other) = default; + + void SetSignerInfo(std::shared_ptr signersInfo) { m_signersInfo = signersInfo; } + const std::shared_ptr& SignersInfo() const { return m_signersInfo; } + + // private: + std::vector& Signers() { return m_signers; } + const std::vector& Signers() const { return m_signers; } + + std::vector& SignersCAs() { return m_signersCertificateAuthorities; } + const std::vector& SignersCAs() const { return m_signersCertificateAuthorities; } + + public: + std::vector CertStores; + bool isSigned = false; + bool bSignatureVerifies = false; + PE_Hashs SignedHashs; + FILETIME Timestamp {0}; + + private: + mutable std::shared_ptr m_authenticodeCache; + std::shared_ptr m_signersInfo; + + std::optional> m_signersName; + std::optional> m_signersThumbprint; + std::optional> m_signersCertificateAuthoritiesName; + std::optional> m_signersCertificateAuthoritiesThumbprint; + + std::vector m_signers; + std::vector m_signersCertificateAuthorities; }; -private: - HCERTSTORE m_hMachineStore = INVALID_HANDLE_VALUE; - HANDLE m_hContext = INVALID_HANDLE_VALUE; +public: + Authenticode(); + ~Authenticode(); - WinTrustExtension m_wintrust; + static DWORD ExpectedHashSize(); + + static HRESULT + ExtractCatalogSigners(std::wstring_view catalogPath, std::string_view catalogData, AuthenticodeData& data); + + static Orc::Result VerifySignatureWithCatalogHint( + std::string_view catalogHint, + const Orc::Authenticode::PE_Hashs& peHashes, + Authenticode::AuthenticodeData& data); + + // Catalog based verifications + HRESULT Verify(LPCWSTR szFileName, AuthenticodeData& data); + HRESULT Verify(LPCWSTR szFileName, const std::shared_ptr& pStream, AuthenticodeData& data); + HRESULT VerifyAnySignatureWithCatalogs(LPCWSTR szFileName, const PE_Hashs& hashs, AuthenticodeData& data); + + // Security directory verification + HRESULT Verify(LPCWSTR szFileName, const CBinaryBuffer& secdir, const PE_Hashs& hashs, AuthenticodeData& data); + HRESULT SignatureSize(LPCWSTR szFileName, const CBinaryBuffer& secdir, DWORD& cbSize); + + std::shared_ptr& Cache() { return m_authenticodeCache; } + void SetCache(std::shared_ptr cache) { m_authenticodeCache = std::move(cache); } + +private: + static HRESULT ExtractCatalogSigners( + std::string_view catalog, + std::vector& pSigners, + std::vector& pCAs, + std::vector& certStores); HRESULT FindCatalogForHash(const CBinaryBuffer& hash, bool& isCatalogSigned, HCATINFO& hCatalog); HRESULT EvaluateCheck(LONG lStatus, AuthenticodeData& data); HRESULT VerifyEmbeddedSignature(LPCWSTR szFileName, HANDLE hFile, AuthenticodeData& data); + HRESULT VerifySignatureWithCatalogs( LPCWSTR szFileName, const CBinaryBuffer& hash, @@ -130,38 +242,11 @@ class Authenticode std::vector& pCA, std::vector& certStores); -public: - Authenticode(); - - static DWORD ExpectedHashSize(); - - static HRESULT ExtractCatalogSigners( - LPCWSTR szCatalogFile, - std::vector& pSigners, - std::vector& pCAs, - std::vector& certStores); - - static HRESULT ExtractCatalogSigners( - std::string_view catalog, - std::vector& pSigners, - std::vector& pCAs, - std::vector& certStores); - - static Orc::Result VerifySignatureWithCatalogHint( - std::string_view catalogHint, - const Orc::Authenticode::PE_Hashs& peHashes, - Authenticode::AuthenticodeData& data); - - // Catalog based verifications - HRESULT Verify(LPCWSTR szFileName, AuthenticodeData& data); - HRESULT Verify(LPCWSTR szFileName, const std::shared_ptr& pStream, AuthenticodeData& data); - HRESULT VerifyAnySignatureWithCatalogs(LPCWSTR szFileName, const PE_Hashs& hashs, AuthenticodeData& data); - - // Security directory verification - HRESULT Verify(LPCWSTR szFileName, const CBinaryBuffer& secdir, const PE_Hashs& hashs, AuthenticodeData& data); - HRESULT SignatureSize(LPCWSTR szFileName, const CBinaryBuffer& secdir, DWORD& cbSize); - - ~Authenticode(); +private: + HCERTSTORE m_hMachineStore = INVALID_HANDLE_VALUE; + HANDLE m_hContext = INVALID_HANDLE_VALUE; + WinTrustExtension m_wintrust; + std::shared_ptr m_authenticodeCache; }; } // namespace Orc diff --git a/src/OrcLib/DataDetails.h b/src/OrcLib/DataDetails.h index 4ea08886..c34caf6b 100644 --- a/src/OrcLib/DataDetails.h +++ b/src/OrcLib/DataDetails.h @@ -180,6 +180,8 @@ class DataDetails }; const Authenticode::AuthenticodeData& GetAuthenticodeData() const { return m_AuthData; }; + Authenticode::AuthenticodeData& GetAuthenticodeData() { return m_AuthData; }; + bool HasAuthenticodeData() const { return m_bHasAuthData; }; HRESULT SetVersionInfoBlock(CBinaryBuffer&& buffer) diff --git a/src/OrcLib/FileInfo.cpp b/src/OrcLib/FileInfo.cpp index 737dedab..eda67949 100644 --- a/src/OrcLib/FileInfo.cpp +++ b/src/OrcLib/FileInfo.cpp @@ -921,7 +921,7 @@ HRESULT FileInfo::OpenAuthenticode() if (FAILED(hr = CheckHash())) return hr; - Authenticode::AuthenticodeData data; + Authenticode::AuthenticodeData data(m_codeVerifyTrust.Cache()); if (IsDirectory() || !m_PEInfo.HasPEHeader()) { GetDetails()->SetAuthenticodeData(std::move(data)); // Setting empty Authenticode data @@ -1074,7 +1074,10 @@ HRESULT FileInfo::WriteOwnerSid(ITableOutput& output) if (ERROR_SUCCESS != dwStatus) return HRESULT_FROM_WIN32(dwStatus); - BOOST_SCOPE_EXIT(&pSD) { ::LocalFree(pSD); } + BOOST_SCOPE_EXIT(&pSD) + { + ::LocalFree(pSD); + } BOOST_SCOPE_EXIT_END WCHAR* StringSid = NULL; @@ -1100,7 +1103,10 @@ HRESULT FileInfo::WriteOwner(ITableOutput& output) if (ERROR_SUCCESS != dwStatus) return HRESULT_FROM_WIN32(dwStatus); - BOOST_SCOPE_EXIT(&pSD) { ::LocalFree(pSD); } + BOOST_SCOPE_EXIT(&pSD) + { + ::LocalFree(pSD); + } BOOST_SCOPE_EXIT_END #define MAX_NAME 512 @@ -1546,7 +1552,10 @@ HRESULT FileInfo::WriteSecurityDirectory(ITableOutput& output) if (szWstr == nullptr) return E_OUTOFMEMORY; - BOOST_SCOPE_EXIT(&szWstr) { ::free(szWstr); } + BOOST_SCOPE_EXIT(&szWstr) + { + ::free(szWstr); + } BOOST_SCOPE_EXIT_END CryptBinaryToStringW( @@ -1646,32 +1655,31 @@ HRESULT FileInfo::WriteAuthenticodeSigner(ITableOutput& output) if (FAILED(hr = CheckAuthenticodeData())) { if (hr == HRESULT_FROM_WIN32(ERROR_DIRECTORY) || hr == HRESULT_FROM_WIN32(ERROR_NO_DATA)) + { return output.WriteNothing(); + } + return hr; } - auto& signers = GetDetails()->GetAuthenticodeData().Signers; + const auto& signers = GetDetails()->GetAuthenticodeData().SignersName(); if (signers.empty()) + { return output.WriteNothing(); + } bool bIsFirst = true; - std::wstringstream signerstream; - - for (auto signer : signers) + for (const auto& name : signers) { - WCHAR szNameString[ORC_MAX_PATH]; - ZeroMemory(szNameString, ORC_MAX_PATH * sizeof(WCHAR)); - - CertGetNameString(signer, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0L, NULL, szNameString, ORC_MAX_PATH); if (!bIsFirst) { - signerstream << L";" << szNameString; + signerstream << L";" << name; } else { - signerstream << szNameString; + signerstream << name; bIsFirst = false; } } @@ -1686,39 +1694,35 @@ HRESULT FileInfo::WriteAuthenticodeSignerThumbprint(ITableOutput& output) if (FAILED(hr = CheckAuthenticodeData())) { if (hr == HRESULT_FROM_WIN32(ERROR_DIRECTORY) || hr == HRESULT_FROM_WIN32(ERROR_NO_DATA)) + { return output.WriteNothing(); + } + return hr; } - auto& signers = GetDetails()->GetAuthenticodeData().Signers; + const auto& thumbprints = GetDetails()->GetAuthenticodeData().SignersThumbprint(); - if (signers.empty()) + if (thumbprints.empty()) + { return output.WriteNothing(); + } bool bIsFirst = true; - std::wstringstream signerstream; - - for (auto signer : signers) + for (const auto& thumbprint : thumbprints) { - BYTE Thumbprint[BYTES_IN_SHA256_HASH]; - DWORD cbThumbprint = BYTES_IN_SHA256_HASH; - if (!CertGetCertificateContextProperty(signer, CERT_HASH_PROP_ID, Thumbprint, &cbThumbprint)) + if (!bIsFirst) { - Log::Debug("Failed to extract certificate thumbprint"); + signerstream << L";"; } - else + + for (UINT i = 0; i < thumbprint.size(); i++) { - if (!bIsFirst) - { - signerstream << L";"; - } - for (UINT i = 0; i < cbThumbprint; i++) - { - signerstream << std::setfill(L'0') << std::setw(2) << std::hex << Thumbprint[i]; - } - bIsFirst = false; + signerstream << std::setfill(L'0') << std::setw(2) << std::hex << thumbprint[i]; } + + bIsFirst = false; } return output.WriteString(signerstream.str()); @@ -1731,35 +1735,35 @@ HRESULT FileInfo::WriteAuthenticodeCA(ITableOutput& output) if (FAILED(hr = CheckAuthenticodeData())) { if (hr == HRESULT_FROM_WIN32(ERROR_DIRECTORY) || hr == HRESULT_FROM_WIN32(ERROR_NO_DATA)) + { return output.WriteNothing(); + } + return hr; } - auto& signersCAs = GetDetails()->GetAuthenticodeData().SignersCAs; + const auto& signersCAs = GetDetails()->GetAuthenticodeData().SignersCertificateAuthoritiesName(); if (signersCAs.empty()) + { return output.WriteNothing(); + } bool bIsFirst = true; - std::wstringstream castream; - - for (auto CA : signersCAs) + for (const auto& name : signersCAs) { - WCHAR szNameString[ORC_MAX_PATH]; - ZeroMemory(szNameString, ORC_MAX_PATH * sizeof(WCHAR)); - - CertGetNameString(CA, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0L, NULL, szNameString, ORC_MAX_PATH); if (!bIsFirst) { - castream << L";" << szNameString; + castream << L";" << name; } else { - castream << szNameString; + castream << name; bIsFirst = false; } } + return output.WriteString(castream.str()); } @@ -1770,39 +1774,34 @@ HRESULT FileInfo::WriteAuthenticodeCAThumbprint(ITableOutput& output) if (FAILED(hr = CheckAuthenticodeData())) { if (hr == HRESULT_FROM_WIN32(ERROR_DIRECTORY) || hr == HRESULT_FROM_WIN32(ERROR_NO_DATA)) + { return output.WriteNothing(); + } + return hr; } - auto& signersCAs = GetDetails()->GetAuthenticodeData().SignersCAs; - - if (signersCAs.empty()) + const auto& thumbprints = GetDetails()->GetAuthenticodeData().SignersCertificateAuthoritiesThumbprint(); + if (thumbprints.empty()) + { return output.WriteNothing(); + } bool bIsFirst = true; - std::wstringstream castream; - - for (auto ca : signersCAs) + for (auto thumbprint : thumbprints) { - BYTE Thumbprint[BYTES_IN_SHA256_HASH]; - DWORD cbThumbprint = BYTES_IN_SHA256_HASH; - if (!CertGetCertificateContextProperty(ca, CERT_HASH_PROP_ID, Thumbprint, &cbThumbprint)) + if (!bIsFirst) { - Log::Debug("Failed to extract certificate thumbprint"); + castream << L";"; } - else + + for (UINT i = 0; i < thumbprint.size(); i++) { - if (!bIsFirst) - { - castream << L";"; - } - for (UINT i = 0; i < cbThumbprint; i++) - { - castream << std::setfill(L'0') << std::setw(2) << std::hex << Thumbprint[i]; - } - bIsFirst = false; + castream << std::setfill(L'0') << std::setw(2) << std::hex << thumbprint[i]; } + + bIsFirst = false; } return output.WriteString(castream.str()); diff --git a/src/OrcLib/FileInfo.h b/src/OrcLib/FileInfo.h index 714e1d5b..a706547d 100644 --- a/src/OrcLib/FileInfo.h +++ b/src/OrcLib/FileInfo.h @@ -73,7 +73,6 @@ class FileInfo : public IIntentionsHandler HRESULT CheckFirstBytes(); HRESULT CheckFileHandle(); HRESULT CheckHash(); - HRESULT CheckFuzzyHash(); HRESULT CheckAuthenticodeData(); protected: diff --git a/tests/OrcLibTest/usn_walker_test.cpp b/tests/OrcLibTest/usn_walker_test.cpp index 8485257f..917813be 100644 --- a/tests/OrcLibTest/usn_walker_test.cpp +++ b/tests/OrcLibTest/usn_walker_test.cpp @@ -63,7 +63,14 @@ TEST_CLASS(USNWalkerTest) std::vector filters; Authenticode authenticode; - USNRecordFileInfo fi(L"Test", volreader, Intentions::FILEINFO_FILESIZE, filters, szFullName, pElt, authenticode); + USNRecordFileInfo fi( + L"Test", + volreader, + Intentions::FILEINFO_FILESIZE, + filters, + szFullName, + pElt, + authenticode); if (fi.IsDirectory()) {