aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2021-05-03 14:41:33 -0500
committerSean Hall <r.sean.hall@gmail.com>2021-05-11 19:11:19 -0500
commit2f4287fdcee83b30e0f7f3ce548bcdff2ee85e1f (patch)
treef2413b7c18e22ecc4398c28df5acdfd9feebd310 /src
parentcd921db764df9578733c85c29e8c6c368f4c7e78 (diff)
downloadwix-2f4287fdcee83b30e0f7f3ce548bcdff2ee85e1f.tar.gz
wix-2f4287fdcee83b30e0f7f3ce548bcdff2ee85e1f.tar.bz2
wix-2f4287fdcee83b30e0f7f3ce548bcdff2ee85e1f.zip
Bring back Burn's implementation of signature verification.
partial #6447
Diffstat (limited to 'src')
-rw-r--r--src/burn/engine/cache.cpp131
-rw-r--r--src/burn/engine/payload.cpp24
-rw-r--r--src/burn/engine/payload.h5
-rw-r--r--src/burn/engine/precomp.h1
-rw-r--r--src/burn/stub/stub.vcxproj4
-rw-r--r--src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj2
6 files changed, 164 insertions, 3 deletions
diff --git a/src/burn/engine/cache.cpp b/src/burn/engine/cache.cpp
index 44267cbc..61416aed 100644
--- a/src/burn/engine/cache.cpp
+++ b/src/burn/engine/cache.cpp
@@ -17,6 +17,11 @@ static LPWSTR vsczDefaultUserPackageCache = NULL;
17static LPWSTR vsczDefaultMachinePackageCache = NULL; 17static LPWSTR vsczDefaultMachinePackageCache = NULL;
18static LPWSTR vsczCurrentMachinePackageCache = NULL; 18static LPWSTR vsczCurrentMachinePackageCache = NULL;
19 19
20static HRESULT CacheVerifyPayloadSignature(
21 __in BURN_PAYLOAD* pPayload,
22 __in_z LPCWSTR wzUnverifiedPayloadPath,
23 __in HANDLE hFile
24 );
20static HRESULT CalculateWorkingFolder( 25static HRESULT CalculateWorkingFolder(
21 __in_z LPCWSTR wzBundleId, 26 __in_z LPCWSTR wzBundleId,
22 __deref_out_z LPWSTR* psczWorkingFolder 27 __deref_out_z LPWSTR* psczWorkingFolder
@@ -131,6 +136,10 @@ static HRESULT VerifyHash(
131 __in LPPROGRESS_ROUTINE pfnProgress, 136 __in LPPROGRESS_ROUTINE pfnProgress,
132 __in LPVOID pContext 137 __in LPVOID pContext
133 ); 138 );
139static HRESULT VerifyPayloadAgainstCertChain(
140 __in BURN_PAYLOAD* pPayload,
141 __in PCCERT_CHAIN_CONTEXT pChainContext
142 );
134static HRESULT SendCacheBeginMessage( 143static HRESULT SendCacheBeginMessage(
135 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, 144 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
136 __in LPVOID pContext, 145 __in LPVOID pContext,
@@ -1133,6 +1142,56 @@ LExit:
1133 return hr; 1142 return hr;
1134} 1143}
1135 1144
1145static HRESULT CacheVerifyPayloadSignature(
1146 __in BURN_PAYLOAD* pPayload,
1147 __in_z LPCWSTR wzUnverifiedPayloadPath,
1148 __in HANDLE hFile
1149 )
1150{
1151 HRESULT hr = S_OK;
1152 LONG er = ERROR_SUCCESS;
1153
1154 GUID guidAuthenticode = WINTRUST_ACTION_GENERIC_VERIFY_V2;
1155 WINTRUST_FILE_INFO wfi = { };
1156 WINTRUST_DATA wtd = { };
1157 CRYPT_PROVIDER_DATA* pProviderData = NULL;
1158 CRYPT_PROVIDER_SGNR* pSigner = NULL;
1159
1160 // Verify the payload assuming online.
1161 wfi.cbStruct = sizeof(wfi);
1162 wfi.pcwszFilePath = wzUnverifiedPayloadPath;
1163 wfi.hFile = hFile;
1164
1165 wtd.cbStruct = sizeof(wtd);
1166 wtd.dwUnionChoice = WTD_CHOICE_FILE;
1167 wtd.pFile = &wfi;
1168 wtd.dwStateAction = WTD_STATEACTION_VERIFY;
1169 wtd.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
1170 wtd.dwUIChoice = WTD_UI_NONE;
1171
1172 er = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &guidAuthenticode, &wtd);
1173 if (er)
1174 {
1175 // Verify the payload assuming offline.
1176 wtd.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL;
1177
1178 er = ::WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &guidAuthenticode, &wtd);
1179 ExitOnWin32Error(er, hr, "Failed authenticode verification of payload: %ls", wzUnverifiedPayloadPath);
1180 }
1181
1182 pProviderData = ::WTHelperProvDataFromStateData(wtd.hWVTStateData);
1183 ExitOnNullWithLastError(pProviderData, hr, "Failed to get provider state from authenticode certificate.");
1184
1185 pSigner = ::WTHelperGetProvSignerFromChain(pProviderData, 0, FALSE, 0);
1186 ExitOnNullWithLastError(pSigner, hr, "Failed to get signer chain from authenticode certificate.");
1187
1188 hr = VerifyPayloadAgainstCertChain(pPayload, pSigner->pChainContext);
1189 ExitOnFailure(hr, "Failed to verify expected payload against actual certificate chain.");
1190
1191LExit:
1192 return hr;
1193}
1194
1136extern "C" void CacheCleanup( 1195extern "C" void CacheCleanup(
1137 __in BOOL fPerMachine, 1196 __in BOOL fPerMachine,
1138 __in_z LPCWSTR wzBundleId 1197 __in_z LPCWSTR wzBundleId
@@ -1563,6 +1622,10 @@ static HRESULT VerifyThenTransferPayload(
1563 1622
1564 switch (pPayload->verification) 1623 switch (pPayload->verification)
1565 { 1624 {
1625 case BURN_PAYLOAD_VERIFICATION_AUTHENTICODE:
1626 hr = CacheVerifyPayloadSignature(pPayload, wzUnverifiedPayloadPath, hFile);
1627 ExitOnFailure(hr, "Failed to verify payload signature: %ls", wzCachedPath);
1628 break;
1566 case BURN_PAYLOAD_VERIFICATION_HASH: 1629 case BURN_PAYLOAD_VERIFICATION_HASH:
1567 hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, TRUE, wzUnverifiedPayloadPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext); 1630 hr = VerifyHash(pPayload->pbHash, pPayload->cbHash, pPayload->qwFileSize, TRUE, wzUnverifiedPayloadPath, hFile, BURN_CACHE_STEP_HASH, pfnCacheMessageHandler, pfnProgress, pContext);
1568 ExitOnFailure(hr, "Failed to verify payload hash: %ls", wzCachedPath); 1631 ExitOnFailure(hr, "Failed to verify payload hash: %ls", wzCachedPath);
@@ -1705,6 +1768,10 @@ static HRESULT VerifyFileAgainstPayload(
1705 1768
1706 switch (pPayload->verification) 1769 switch (pPayload->verification)
1707 { 1770 {
1771 case BURN_PAYLOAD_VERIFICATION_AUTHENTICODE:
1772 hr = CacheVerifyPayloadSignature(pPayload, wzVerifyPath, hFile);
1773 ExitOnFailure(hr, "Failed to verify signature of payload: %ls", pPayload->sczKey);
1774 break;
1708 case BURN_PAYLOAD_VERIFICATION_HASH: 1775 case BURN_PAYLOAD_VERIFICATION_HASH:
1709 fVerifyFileSize = TRUE; 1776 fVerifyFileSize = TRUE;
1710 1777
@@ -2144,6 +2211,70 @@ LExit:
2144 return hr; 2211 return hr;
2145} 2212}
2146 2213
2214static HRESULT VerifyPayloadAgainstCertChain(
2215 __in BURN_PAYLOAD* pPayload,
2216 __in PCCERT_CHAIN_CONTEXT pChainContext
2217 )
2218{
2219 HRESULT hr = S_OK;
2220 PCCERT_CONTEXT pChainElementCertContext = NULL;
2221
2222 BYTE rgbPublicKeyIdentifier[SHA1_HASH_LEN] = { };
2223 DWORD cbPublicKeyIdentifier = sizeof(rgbPublicKeyIdentifier);
2224 BYTE* pbThumbprint = NULL;
2225 DWORD cbThumbprint = 0;
2226
2227 // Walk up the chain looking for a certificate in the chain that matches our expected public key identifier
2228 // and thumbprint (if a thumbprint was provided).
2229 HRESULT hrChainVerification = E_NOTFOUND; // assume we won't find a match.
2230 for (DWORD i = 0; i < pChainContext->rgpChain[0]->cElement; ++i)
2231 {
2232 pChainElementCertContext = pChainContext->rgpChain[0]->rgpElement[i]->pCertContext;
2233
2234 // Get the certificate's public key identifier.
2235 if (!::CryptHashPublicKeyInfo(NULL, CALG_SHA1, 0, X509_ASN_ENCODING, &pChainElementCertContext->pCertInfo->SubjectPublicKeyInfo, rgbPublicKeyIdentifier, &cbPublicKeyIdentifier))
2236 {
2237 ExitWithLastError(hr, "Failed to get certificate public key identifier.");
2238 }
2239
2240 // Compare the certificate's public key identifier with the payload's public key identifier. If they
2241 // match, we're one step closer to the a positive result.
2242 if (pPayload->cbCertificateRootPublicKeyIdentifier == cbPublicKeyIdentifier &&
2243 0 == memcmp(pPayload->pbCertificateRootPublicKeyIdentifier, rgbPublicKeyIdentifier, cbPublicKeyIdentifier))
2244 {
2245 // If the payload specified a thumbprint for the certificate, verify it.
2246 if (pPayload->pbCertificateRootThumbprint)
2247 {
2248 hr = CertReadProperty(pChainElementCertContext, CERT_SHA1_HASH_PROP_ID, &pbThumbprint, &cbThumbprint);
2249 ExitOnFailure(hr, "Failed to read certificate thumbprint.");
2250
2251 if (pPayload->cbCertificateRootThumbprint == cbThumbprint &&
2252 0 == memcmp(pPayload->pbCertificateRootThumbprint, pbThumbprint, cbThumbprint))
2253 {
2254 // If we got here, we found that our payload public key identifier and thumbprint
2255 // matched an element in the certficate chain.
2256 hrChainVerification = S_OK;
2257 break;
2258 }
2259
2260 ReleaseNullMem(pbThumbprint);
2261 }
2262 else // no thumbprint match necessary so we're good to go.
2263 {
2264 hrChainVerification = S_OK;
2265 break;
2266 }
2267 }
2268 }
2269 hr = hrChainVerification;
2270 ExitOnFailure(hr, "Failed to find expected public key in certificate chain.");
2271
2272LExit:
2273 ReleaseMem(pbThumbprint);
2274
2275 return hr;
2276}
2277
2147static HRESULT SendCacheBeginMessage( 2278static HRESULT SendCacheBeginMessage(
2148 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler, 2279 __in PFN_BURNCACHEMESSAGEHANDLER pfnCacheMessageHandler,
2149 __in LPVOID pContext, 2280 __in LPVOID pContext,
diff --git a/src/burn/engine/payload.cpp b/src/burn/engine/payload.cpp
index 392a3dd4..84c32eec 100644
--- a/src/burn/engine/payload.cpp
+++ b/src/burn/engine/payload.cpp
@@ -132,6 +132,28 @@ extern "C" HRESULT PayloadsParseFromXml(
132 fValidFileSize = TRUE; 132 fValidFileSize = TRUE;
133 } 133 }
134 134
135 // @CertificateAuthorityKeyIdentifier
136 hr = XmlGetAttributeEx(pixnNode, L"CertificateRootPublicKeyIdentifier", &scz);
137 if (E_NOTFOUND != hr)
138 {
139 ExitOnFailure(hr, "Failed to get @CertificateRootPublicKeyIdentifier.");
140
141 hr = StrAllocHexDecode(scz, &pPayload->pbCertificateRootPublicKeyIdentifier, &pPayload->cbCertificateRootPublicKeyIdentifier);
142 ExitOnFailure(hr, "Failed to hex decode @CertificateRootPublicKeyIdentifier.");
143
144 pPayload->verification = BURN_PAYLOAD_VERIFICATION_AUTHENTICODE;
145 }
146
147 // @CertificateThumbprint
148 hr = XmlGetAttributeEx(pixnNode, L"CertificateRootThumbprint", &scz);
149 if (E_NOTFOUND != hr)
150 {
151 ExitOnFailure(hr, "Failed to get @CertificateRootThumbprint.");
152
153 hr = StrAllocHexDecode(scz, &pPayload->pbCertificateRootThumbprint, &pPayload->cbCertificateRootThumbprint);
154 ExitOnFailure(hr, "Failed to hex decode @CertificateRootThumbprint.");
155 }
156
135 // @Hash 157 // @Hash
136 hr = XmlGetAttributeEx(pixnNode, L"Hash", &scz); 158 hr = XmlGetAttributeEx(pixnNode, L"Hash", &scz);
137 if (E_NOTFOUND != hr) 159 if (E_NOTFOUND != hr)
@@ -191,6 +213,8 @@ extern "C" void PayloadUninitialize(
191 ReleaseStr(pPayload->sczKey); 213 ReleaseStr(pPayload->sczKey);
192 ReleaseStr(pPayload->sczFilePath); 214 ReleaseStr(pPayload->sczFilePath);
193 ReleaseMem(pPayload->pbHash); 215 ReleaseMem(pPayload->pbHash);
216 ReleaseMem(pPayload->pbCertificateRootThumbprint);
217 ReleaseMem(pPayload->pbCertificateRootPublicKeyIdentifier);
194 ReleaseStr(pPayload->sczSourcePath); 218 ReleaseStr(pPayload->sczSourcePath);
195 ReleaseStr(pPayload->sczLocalFilePath); 219 ReleaseStr(pPayload->sczLocalFilePath);
196 ReleaseStr(pPayload->downloadSource.sczUrl); 220 ReleaseStr(pPayload->downloadSource.sczUrl);
diff --git a/src/burn/engine/payload.h b/src/burn/engine/payload.h
index 6fc6de5e..c12fbe66 100644
--- a/src/burn/engine/payload.h
+++ b/src/burn/engine/payload.h
@@ -26,6 +26,7 @@ enum BURN_PAYLOAD_STATE
26enum BURN_PAYLOAD_VERIFICATION 26enum BURN_PAYLOAD_VERIFICATION
27{ 27{
28 BURN_PAYLOAD_VERIFICATION_NONE, 28 BURN_PAYLOAD_VERIFICATION_NONE,
29 BURN_PAYLOAD_VERIFICATION_AUTHENTICODE,
29 BURN_PAYLOAD_VERIFICATION_HASH, 30 BURN_PAYLOAD_VERIFICATION_HASH,
30 BURN_PAYLOAD_VERIFICATION_UPDATE_BUNDLE, 31 BURN_PAYLOAD_VERIFICATION_UPDATE_BUNDLE,
31}; 32};
@@ -41,6 +42,10 @@ typedef struct _BURN_PAYLOAD
41 DWORD64 qwFileSize; 42 DWORD64 qwFileSize;
42 LPWSTR sczFilePath; // file path relative to the execute location 43 LPWSTR sczFilePath; // file path relative to the execute location
43 44
45 BYTE* pbCertificateRootPublicKeyIdentifier;
46 DWORD cbCertificateRootPublicKeyIdentifier;
47 BYTE* pbCertificateRootThumbprint;
48 DWORD cbCertificateRootThumbprint;
44 BYTE* pbHash; 49 BYTE* pbHash;
45 DWORD cbHash; 50 DWORD cbHash;
46 BURN_PAYLOAD_VERIFICATION verification; 51 BURN_PAYLOAD_VERIFICATION verification;
diff --git a/src/burn/engine/precomp.h b/src/burn/engine/precomp.h
index 11b594da..c1822381 100644
--- a/src/burn/engine/precomp.h
+++ b/src/burn/engine/precomp.h
@@ -28,6 +28,7 @@
28#include <apputil.h> 28#include <apputil.h>
29#include <buffutil.h> 29#include <buffutil.h>
30#include <cabutil.h> 30#include <cabutil.h>
31#include <certutil.h>
31#include <cryputil.h> 32#include <cryputil.h>
32#include <dirutil.h> 33#include <dirutil.h>
33#include <fileutil.h> 34#include <fileutil.h>
diff --git a/src/burn/stub/stub.vcxproj b/src/burn/stub/stub.vcxproj
index b75f17f4..e7bdf6e3 100644
--- a/src/burn/stub/stub.vcxproj
+++ b/src/burn/stub/stub.vcxproj
@@ -56,14 +56,14 @@
56 56
57 <PropertyGroup> 57 <PropertyGroup>
58 <ProjectAdditionalIncludeDirectories>$(ProjectDir)..\engine\inc</ProjectAdditionalIncludeDirectories> 58 <ProjectAdditionalIncludeDirectories>$(ProjectDir)..\engine\inc</ProjectAdditionalIncludeDirectories>
59 <ProjectAdditionalLinkLibraries>cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wuguid.lib;engine.res</ProjectAdditionalLinkLibraries> 59 <ProjectAdditionalLinkLibraries>cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wintrust.lib;wuguid.lib;engine.res</ProjectAdditionalLinkLibraries>
60 </PropertyGroup> 60 </PropertyGroup>
61 61
62 <ItemDefinitionGroup> 62 <ItemDefinitionGroup>
63 <Link> 63 <Link>
64 <SwapRunFromCD>true</SwapRunFromCD> 64 <SwapRunFromCD>true</SwapRunFromCD>
65 <SwapRunFromNET>true</SwapRunFromNET> 65 <SwapRunFromNET>true</SwapRunFromNET>
66 <DelayLoadDLLs>cabinet.dll;crypt32.dll;msi.dll;shlwapi.dll;version.dll;wininet.dll</DelayLoadDLLs> 66 <DelayLoadDLLs>cabinet.dll;crypt32.dll;msi.dll;shlwapi.dll;version.dll;wininet.dll;wintrust.dll</DelayLoadDLLs>
67 </Link> 67 </Link>
68 </ItemDefinitionGroup> 68 </ItemDefinitionGroup>
69 69
diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj
index 7ee27258..85481821 100644
--- a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj
+++ b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj
@@ -40,7 +40,7 @@
40 40
41 <PropertyGroup> 41 <PropertyGroup>
42 <ProjectAdditionalIncludeDirectories>$(ProjectAdditionalIncludeDirectories);..\..\engine;..\..\..\api\burn\WixToolset.BootstrapperCore.Native\inc;..\..\..\libs\dutil\WixToolset.Dutil\inc</ProjectAdditionalIncludeDirectories> 42 <ProjectAdditionalIncludeDirectories>$(ProjectAdditionalIncludeDirectories);..\..\engine;..\..\..\api\burn\WixToolset.BootstrapperCore.Native\inc;..\..\..\libs\dutil\WixToolset.Dutil\inc</ProjectAdditionalIncludeDirectories>
43 <ProjectAdditionalLinkLibraries>cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;$(RootBuildFolder)libs\$(Configuration)\$(PlatformToolset)\$(PlatformTarget)\dutil.lib</ProjectAdditionalLinkLibraries> 43 <ProjectAdditionalLinkLibraries>cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wintrust.lib;$(RootBuildFolder)libs\$(Configuration)\$(PlatformToolset)\$(PlatformTarget)\dutil.lib</ProjectAdditionalLinkLibraries>
44 </PropertyGroup> 44 </PropertyGroup>
45 45
46 <ItemGroup> 46 <ItemGroup>