зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1473175: Add support for catalog files to DLL signature verifications; r=mhowell
This commit is contained in:
Родитель
7515739963
Коммит
09a7297c55
|
@ -16,16 +16,31 @@
|
|||
#include "mozilla/mozalloc.h"
|
||||
#endif
|
||||
|
||||
// We need Windows 8 functions and structures to be able to verify SHA-256.
|
||||
#if defined(_WIN32_WINNT)
|
||||
#undef _WIN32_WINNT
|
||||
#define _WIN32_WINNT _WIN32_WINNT_WIN8
|
||||
#endif // defined(_WIN32_WINNT)
|
||||
#if defined(NTDDI_VERSION)
|
||||
#undef NTDDI_VERSION
|
||||
#define NTDDI_VERSION NTDDI_WIN8
|
||||
#endif // defined(NTDDI_VERSION)
|
||||
|
||||
#include "Authenticode.h"
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/DynamicallyLinkedFunctionPtr.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
#include "nsWindowsHelpers.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <softpub.h>
|
||||
#include <wincrypt.h>
|
||||
#include <wintrust.h>
|
||||
#include <mscat.h>
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -55,13 +70,33 @@ struct CertContextDeleter
|
|||
}
|
||||
};
|
||||
|
||||
struct CATAdminContextDeleter
|
||||
{
|
||||
typedef HCATADMIN pointer;
|
||||
void operator()(pointer aCtx)
|
||||
{
|
||||
static const mozilla::DynamicallyLinkedFunctionPtr<
|
||||
decltype(&::CryptCATAdminReleaseContext)>
|
||||
pCryptCATAdminReleaseContext(L"wintrust.dll",
|
||||
"CryptCATAdminReleaseContext");
|
||||
|
||||
MOZ_ASSERT(!!pCryptCATAdminReleaseContext);
|
||||
if (!pCryptCATAdminReleaseContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
pCryptCATAdminReleaseContext(aCtx, 0);
|
||||
}
|
||||
};
|
||||
|
||||
typedef mozilla::UniquePtr<HCERTSTORE, CertStoreDeleter> CertStoreUniquePtr;
|
||||
typedef mozilla::UniquePtr<HCRYPTMSG, CryptMsgDeleter> CryptMsgUniquePtr;
|
||||
typedef mozilla::UniquePtr<const CERT_CONTEXT, CertContextDeleter> CertContextUniquePtr;
|
||||
typedef mozilla::UniquePtr<HCATADMIN, CATAdminContextDeleter> CATAdminContextUniquePtr;
|
||||
|
||||
static const DWORD kEncodingTypes = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
|
||||
|
||||
class SignedBinary
|
||||
class SignedBinary final
|
||||
{
|
||||
public:
|
||||
explicit SignedBinary(const wchar_t* aFilePath);
|
||||
|
@ -80,34 +115,31 @@ public:
|
|||
|
||||
private:
|
||||
bool VerifySignature(const wchar_t* aFilePath);
|
||||
bool QueryObject(const wchar_t* aFilePath);
|
||||
static bool VerifySignatureInternal(WINTRUST_DATA& aTrustData);
|
||||
|
||||
private:
|
||||
enum class TrustSource
|
||||
{
|
||||
eNone,
|
||||
eEmbedded,
|
||||
eCatalog
|
||||
};
|
||||
|
||||
private:
|
||||
TrustSource mTrustSource;
|
||||
CertStoreUniquePtr mCertStore;
|
||||
CryptMsgUniquePtr mCryptMsg;
|
||||
CertContextUniquePtr mCertCtx;
|
||||
};
|
||||
|
||||
SignedBinary::SignedBinary(const wchar_t* aFilePath)
|
||||
: mTrustSource(TrustSource::eNone)
|
||||
{
|
||||
if (!VerifySignature(aFilePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD encodingType, contentType, formatType;
|
||||
HCERTSTORE rawCertStore;
|
||||
HCRYPTMSG rawCryptMsg;
|
||||
BOOL result = CryptQueryObject(CERT_QUERY_OBJECT_FILE, aFilePath,
|
||||
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
|
||||
CERT_QUERY_FORMAT_FLAG_BINARY, 0,
|
||||
&encodingType, &contentType, &formatType,
|
||||
&rawCertStore, &rawCryptMsg, nullptr);
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
mCertStore.reset(rawCertStore);
|
||||
mCryptMsg.reset(rawCryptMsg);
|
||||
|
||||
DWORD certInfoLen = 0;
|
||||
BOOL ok = CryptMsgGetParam(mCryptMsg.get(), CMSG_SIGNER_CERT_INFO_PARAM, 0,
|
||||
nullptr, &certInfoLen);
|
||||
|
@ -136,28 +168,212 @@ SignedBinary::SignedBinary(const wchar_t* aFilePath)
|
|||
mCertCtx.reset(certCtx);
|
||||
}
|
||||
|
||||
bool
|
||||
SignedBinary::QueryObject(const wchar_t* aFilePath)
|
||||
{
|
||||
DWORD encodingType, contentType, formatType;
|
||||
HCERTSTORE rawCertStore;
|
||||
HCRYPTMSG rawCryptMsg;
|
||||
BOOL result = ::CryptQueryObject(CERT_QUERY_OBJECT_FILE, aFilePath,
|
||||
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
|
||||
CERT_QUERY_FORMAT_FLAG_BINARY, 0,
|
||||
&encodingType, &contentType, &formatType,
|
||||
&rawCertStore, &rawCryptMsg, nullptr);
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mCertStore.reset(rawCertStore);
|
||||
mCryptMsg.reset(rawCryptMsg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param aTrustData must be a WINTRUST_DATA structure that has been zeroed out
|
||||
* and then populated at least with its |cbStruct|,
|
||||
* |dwUnionChoice|, and appropriate union field. This function
|
||||
* will then populate the remaining fields as appropriate.
|
||||
*/
|
||||
/* static */ bool
|
||||
SignedBinary::VerifySignatureInternal(WINTRUST_DATA& aTrustData)
|
||||
{
|
||||
aTrustData.dwUIChoice = WTD_UI_NONE;
|
||||
aTrustData.fdwRevocationChecks = WTD_REVOKE_NONE;
|
||||
aTrustData.dwStateAction = WTD_STATEACTION_VERIFY;
|
||||
aTrustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
|
||||
|
||||
const HWND hwnd = (HWND) INVALID_HANDLE_VALUE;
|
||||
GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2;
|
||||
LONG result = ::WinVerifyTrust(hwnd, &policyGUID, &aTrustData);
|
||||
|
||||
aTrustData.dwStateAction = WTD_STATEACTION_CLOSE;
|
||||
::WinVerifyTrust(hwnd, &policyGUID, &aTrustData);
|
||||
|
||||
return result == ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
bool
|
||||
SignedBinary::VerifySignature(const wchar_t* aFilePath)
|
||||
{
|
||||
// First, try the binary itself
|
||||
WINTRUST_FILE_INFO fileInfo = {sizeof(fileInfo)};
|
||||
fileInfo.pcwszFilePath = aFilePath;
|
||||
|
||||
WINTRUST_DATA trustData = {sizeof(trustData)};
|
||||
trustData.dwUIChoice = WTD_UI_NONE;
|
||||
trustData.fdwRevocationChecks = WTD_REVOKE_NONE;
|
||||
trustData.dwUnionChoice = WTD_CHOICE_FILE;
|
||||
trustData.pFile = &fileInfo;
|
||||
trustData.dwStateAction = WTD_STATEACTION_VERIFY;
|
||||
trustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
|
||||
|
||||
const HWND hwnd = (HWND) INVALID_HANDLE_VALUE;
|
||||
GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2;
|
||||
LONG result = WinVerifyTrust(hwnd, &policyGUID, &trustData);
|
||||
if (VerifySignatureInternal(trustData)) {
|
||||
mTrustSource = TrustSource::eEmbedded;
|
||||
return QueryObject(aFilePath);
|
||||
}
|
||||
|
||||
trustData.dwStateAction = WTD_STATEACTION_CLOSE;
|
||||
WinVerifyTrust(hwnd, &policyGUID, &trustData);
|
||||
// We didn't find anything in the binary, so now try a catalog file.
|
||||
|
||||
return result == ERROR_SUCCESS;
|
||||
// First, we open a catalog admin context.
|
||||
HCATADMIN rawCatAdmin;
|
||||
|
||||
// Windows 7 also exports the CryptCATAdminAcquireContext2 API, but it does
|
||||
// *not* sign its binaries with SHA-256, so we use the old API in that case.
|
||||
if (mozilla::IsWin8OrLater()) {
|
||||
static const mozilla::DynamicallyLinkedFunctionPtr<decltype(&::CryptCATAdminAcquireContext2)>
|
||||
pCryptCATAdminAcquireContext2(L"wintrust.dll", "CryptCATAdminAcquireContext2");
|
||||
if (!pCryptCATAdminAcquireContext2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CERT_STRONG_SIGN_PARA policy = {sizeof(policy)};
|
||||
policy.dwInfoChoice = CERT_STRONG_SIGN_OID_INFO_CHOICE;
|
||||
policy.pszOID = szOID_CERT_STRONG_SIGN_OS_CURRENT;
|
||||
|
||||
if (!pCryptCATAdminAcquireContext2(&rawCatAdmin, nullptr,
|
||||
BCRYPT_SHA256_ALGORITHM, &policy, 0)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
static const mozilla::DynamicallyLinkedFunctionPtr<decltype(&::CryptCATAdminAcquireContext)>
|
||||
pCryptCATAdminAcquireContext(L"wintrust.dll", "CryptCATAdminAcquireContext");
|
||||
|
||||
if (!pCryptCATAdminAcquireContext ||
|
||||
!pCryptCATAdminAcquireContext(&rawCatAdmin, nullptr, 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CATAdminContextUniquePtr catAdmin(rawCatAdmin);
|
||||
|
||||
// Now we need to hash the file at aFilePath.
|
||||
// Since we're hashing this file, let's open it with a sequential scan hint.
|
||||
HANDLE rawFile = ::CreateFileW(aFilePath, GENERIC_READ, FILE_SHARE_READ,
|
||||
nullptr, OPEN_EXISTING,
|
||||
FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
|
||||
if (rawFile == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsAutoHandle file(rawFile);
|
||||
DWORD hashLen = 0;
|
||||
mozilla::UniquePtr<BYTE[]> hashBuf;
|
||||
|
||||
static const mozilla::DynamicallyLinkedFunctionPtr<decltype(&::CryptCATAdminCalcHashFromFileHandle2)>
|
||||
pCryptCATAdminCalcHashFromFileHandle2(L"wintrust.dll", "CryptCATAdminCalcHashFromFileHandle2");
|
||||
if (pCryptCATAdminCalcHashFromFileHandle2) {
|
||||
if (!pCryptCATAdminCalcHashFromFileHandle2(rawCatAdmin, rawFile, &hashLen,
|
||||
nullptr, 0) &&
|
||||
::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hashBuf = mozilla::MakeUnique<BYTE[]>(hashLen);
|
||||
|
||||
if (!pCryptCATAdminCalcHashFromFileHandle2(rawCatAdmin, rawFile, &hashLen,
|
||||
hashBuf.get(), 0)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
static const mozilla::DynamicallyLinkedFunctionPtr<decltype(&::CryptCATAdminCalcHashFromFileHandle)>
|
||||
pCryptCATAdminCalcHashFromFileHandle(L"wintrust.dll", "CryptCATAdminCalcHashFromFileHandle");
|
||||
|
||||
if (!pCryptCATAdminCalcHashFromFileHandle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pCryptCATAdminCalcHashFromFileHandle(rawFile, &hashLen, nullptr, 0) &&
|
||||
::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hashBuf = mozilla::MakeUnique<BYTE[]>(hashLen);
|
||||
|
||||
if (!pCryptCATAdminCalcHashFromFileHandle(rawFile, &hashLen, hashBuf.get(), 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we've hashed the file, query the catalog system to see if any
|
||||
// catalogs reference a binary with our hash.
|
||||
|
||||
static const mozilla::DynamicallyLinkedFunctionPtr<decltype(&::CryptCATAdminEnumCatalogFromHash)>
|
||||
pCryptCATAdminEnumCatalogFromHash(L"wintrust.dll", "CryptCATAdminEnumCatalogFromHash");
|
||||
if (!pCryptCATAdminEnumCatalogFromHash) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HCATINFO catInfoHdl = pCryptCATAdminEnumCatalogFromHash(rawCatAdmin,
|
||||
hashBuf.get(), hashLen,
|
||||
0, nullptr);
|
||||
if (!catInfoHdl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We found a catalog! Now query for the path to the catalog file.
|
||||
|
||||
static const mozilla::DynamicallyLinkedFunctionPtr<decltype(&::CryptCATCatalogInfoFromContext)>
|
||||
pCryptCATCatalogInfoFromContext(L"wintrust.dll", "CryptCATCatalogInfoFromContext");
|
||||
if (!pCryptCATCatalogInfoFromContext) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CATALOG_INFO_ catInfo = {sizeof(catInfo)};
|
||||
if (!pCryptCATCatalogInfoFromContext(catInfoHdl, &catInfo, 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// WINTRUST_CATALOG_INFO::pcwszMemberTag is commonly set to the string
|
||||
// representation of the file hash, so we build that here.
|
||||
|
||||
DWORD strHashBufLen = (hashLen * 2) + 1;
|
||||
auto strHashBuf = mozilla::MakeUnique<wchar_t[]>(strHashBufLen);
|
||||
if (!::CryptBinaryToStringW(hashBuf.get(), hashLen,
|
||||
CRYPT_STRING_HEXRAW | CRYPT_STRING_NOCRLF,
|
||||
strHashBuf.get(), &strHashBufLen)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now, given the path to the catalog, and the path to the member (ie, the
|
||||
// binary whose hash we are validating), we may now validate. If the
|
||||
// validation is successful, we then QueryObject on the *catalog file*
|
||||
// instead of the binary.
|
||||
|
||||
WINTRUST_CATALOG_INFO wtCatInfo = {sizeof(wtCatInfo)};
|
||||
wtCatInfo.pcwszCatalogFilePath = catInfo.wszCatalogFile;
|
||||
wtCatInfo.pcwszMemberTag = strHashBuf.get();
|
||||
wtCatInfo.pcwszMemberFilePath = aFilePath;
|
||||
wtCatInfo.hMemberFile = rawFile;
|
||||
if (mozilla::IsWin8OrLater()) {
|
||||
wtCatInfo.hCatAdmin = rawCatAdmin;
|
||||
}
|
||||
|
||||
trustData.dwUnionChoice = WTD_CHOICE_CATALOG;
|
||||
trustData.pCatalog = &wtCatInfo;
|
||||
|
||||
if (VerifySignatureInternal(trustData)) {
|
||||
mTrustSource = TrustSource::eCatalog;
|
||||
return QueryObject(catInfo.wszCatalogFile);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
mozilla::UniquePtr<wchar_t[]>
|
||||
|
|
Загрузка…
Ссылка в новой задаче