diff options
author | Sean Hall <r.sean.hall@gmail.com> | 2021-05-03 14:41:33 -0500 |
---|---|---|
committer | Sean Hall <r.sean.hall@gmail.com> | 2021-05-11 19:11:19 -0500 |
commit | 2f4287fdcee83b30e0f7f3ce548bcdff2ee85e1f (patch) | |
tree | f2413b7c18e22ecc4398c28df5acdfd9feebd310 /src | |
parent | cd921db764df9578733c85c29e8c6c368f4c7e78 (diff) | |
download | wix-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.cpp | 131 | ||||
-rw-r--r-- | src/burn/engine/payload.cpp | 24 | ||||
-rw-r--r-- | src/burn/engine/payload.h | 5 | ||||
-rw-r--r-- | src/burn/engine/precomp.h | 1 | ||||
-rw-r--r-- | src/burn/stub/stub.vcxproj | 4 | ||||
-rw-r--r-- | src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj | 2 |
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; | |||
17 | static LPWSTR vsczDefaultMachinePackageCache = NULL; | 17 | static LPWSTR vsczDefaultMachinePackageCache = NULL; |
18 | static LPWSTR vsczCurrentMachinePackageCache = NULL; | 18 | static LPWSTR vsczCurrentMachinePackageCache = NULL; |
19 | 19 | ||
20 | static HRESULT CacheVerifyPayloadSignature( | ||
21 | __in BURN_PAYLOAD* pPayload, | ||
22 | __in_z LPCWSTR wzUnverifiedPayloadPath, | ||
23 | __in HANDLE hFile | ||
24 | ); | ||
20 | static HRESULT CalculateWorkingFolder( | 25 | static 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 | ); |
139 | static HRESULT VerifyPayloadAgainstCertChain( | ||
140 | __in BURN_PAYLOAD* pPayload, | ||
141 | __in PCCERT_CHAIN_CONTEXT pChainContext | ||
142 | ); | ||
134 | static HRESULT SendCacheBeginMessage( | 143 | static 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 | ||
1145 | static 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 | |||
1191 | LExit: | ||
1192 | return hr; | ||
1193 | } | ||
1194 | |||
1136 | extern "C" void CacheCleanup( | 1195 | extern "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 | ||
2214 | static 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 | |||
2272 | LExit: | ||
2273 | ReleaseMem(pbThumbprint); | ||
2274 | |||
2275 | return hr; | ||
2276 | } | ||
2277 | |||
2147 | static HRESULT SendCacheBeginMessage( | 2278 | static 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 | |||
26 | enum BURN_PAYLOAD_VERIFICATION | 26 | enum 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> |