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> |
