aboutsummaryrefslogtreecommitdiff
path: root/src/ext/Iis/ca/scacertexec.cpp
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-05-04 22:48:12 -0700
committerRob Mensching <rob@firegiant.com>2021-05-04 22:48:12 -0700
commit7c8e34de56b3348c5a421cd0cced183e1394c5c7 (patch)
treec2f17867b49e33e0833eae2e1841a00b009c1a15 /src/ext/Iis/ca/scacertexec.cpp
parentc5c87377d99beefe83a3470aab326d12bdf0f8a4 (diff)
downloadwix-7c8e34de56b3348c5a421cd0cced183e1394c5c7.tar.gz
wix-7c8e34de56b3348c5a421cd0cced183e1394c5c7.tar.bz2
wix-7c8e34de56b3348c5a421cd0cced183e1394c5c7.zip
Move Iis.wixext into ext
Diffstat (limited to 'src/ext/Iis/ca/scacertexec.cpp')
-rw-r--r--src/ext/Iis/ca/scacertexec.cpp431
1 files changed, 431 insertions, 0 deletions
diff --git a/src/ext/Iis/ca/scacertexec.cpp b/src/ext/Iis/ca/scacertexec.cpp
new file mode 100644
index 00000000..95870c79
--- /dev/null
+++ b/src/ext/Iis/ca/scacertexec.cpp
@@ -0,0 +1,431 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5#define SIXTY_FOUR_MEG 64 * 1024 * 1024
6
7// prototypes
8static HRESULT ExecuteCertificateOperation(
9 __in MSIHANDLE hInstall,
10 __in SCA_ACTION saAction,
11 __in DWORD dwStoreRoot
12 );
13
14static HRESULT ReadCertificateFile(
15 __in LPCWSTR wzPath,
16 __out BYTE** prgbData,
17 __out DWORD* pcbData
18 );
19
20static HRESULT InstallCertificatePackage(
21 __in HCERTSTORE hStore,
22 __in BOOL fUserCertificateStore,
23 __in LPCWSTR wzName,
24 __in_opt BYTE* rgbData,
25 __in DWORD cbData,
26 __in BOOL fVital,
27 __in_opt LPCWSTR wzPFXPassword
28 );
29
30static HRESULT UninstallCertificatePackage(
31 __in HCERTSTORE hStore,
32 __in BOOL fUserCertificateStore,
33 __in LPCWSTR wzName
34 );
35
36static HRESULT AddCertificate(
37 __in HCERTSTORE hStore,
38 __in PCCERT_CONTEXT pCertContext,
39 __in LPCWSTR wzCertificateUniqueName,
40 __in BOOL fVital
41);
42
43/* ****************************************************************
44 AddUserCertificate - CUSTOM ACTION ENTRY POINT for adding per-user
45 certificates
46
47 * ***************************************************************/
48extern "C" UINT __stdcall AddUserCertificate(
49 __in MSIHANDLE hInstall
50 )
51{
52 HRESULT hr = S_OK;
53 DWORD er = ERROR_SUCCESS;
54
55 hr = WcaInitialize(hInstall, "AddUserCertificate");
56 ExitOnFailure(hr, "Failed to initialize AddUserCertificate.");
57
58 hr = ExecuteCertificateOperation(hInstall, SCA_ACTION_INSTALL, CERT_SYSTEM_STORE_CURRENT_USER);
59 ExitOnFailure(hr, "Failed to install per-user certificate.");
60
61LExit:
62 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
63 return WcaFinalize(er);
64}
65
66
67/* ****************************************************************
68 AddMachineCertificate - CUSTOM ACTION ENTRY POINT for adding
69 per-machine certificates
70
71 * ***************************************************************/
72extern "C" UINT __stdcall AddMachineCertificate(
73 __in MSIHANDLE hInstall
74 )
75{
76 HRESULT hr = S_OK;
77 DWORD er = ERROR_SUCCESS;
78
79 hr = WcaInitialize(hInstall, "AddMachineCertificate");
80 ExitOnFailure(hr, "Failed to initialize AddMachineCertificate.");
81
82 hr = ExecuteCertificateOperation(hInstall, SCA_ACTION_INSTALL, CERT_SYSTEM_STORE_LOCAL_MACHINE);
83 ExitOnFailure(hr, "Failed to install per-machine certificate.");
84
85LExit:
86 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
87 return WcaFinalize(er);
88}
89
90
91/* ****************************************************************
92 DeleteUserCertificate - CUSTOM ACTION ENTRY POINT for deleting
93 per-user certificates
94
95 * ***************************************************************/
96extern "C" UINT __stdcall DeleteUserCertificate(
97 __in MSIHANDLE hInstall
98 )
99{
100 HRESULT hr = S_OK;
101 DWORD er = ERROR_SUCCESS;
102
103 hr = WcaInitialize(hInstall, "DeleteUserCertificate");
104 ExitOnFailure(hr, "Failed to initialize DeleteUserCertificate.");
105
106 hr = ExecuteCertificateOperation(hInstall, SCA_ACTION_UNINSTALL, CERT_SYSTEM_STORE_CURRENT_USER);
107 ExitOnFailure(hr, "Failed to uninstall per-user certificate.");
108
109LExit:
110 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
111 return WcaFinalize(er);
112}
113
114
115/* ****************************************************************
116 DeleteMachineCertificate - CUSTOM ACTION ENTRY POINT for deleting
117 per-machine certificates
118
119 * ***************************************************************/
120extern "C" UINT __stdcall DeleteMachineCertificate(
121 __in MSIHANDLE hInstall
122 )
123{
124 HRESULT hr = S_OK;
125 DWORD er = ERROR_SUCCESS;
126
127 hr = WcaInitialize(hInstall, "DeleteMachineCertificate");
128 ExitOnFailure(hr, "Failed to initialize DeleteMachineCertificate.");
129
130 hr = ExecuteCertificateOperation(hInstall, SCA_ACTION_UNINSTALL, CERT_SYSTEM_STORE_LOCAL_MACHINE);
131 ExitOnFailure(hr, "Failed to uninstall per-machine certificate.");
132
133LExit:
134 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
135 return WcaFinalize(er);
136}
137
138
139static HRESULT ExecuteCertificateOperation(
140 __in MSIHANDLE /*hInstall*/,
141 __in SCA_ACTION saAction,
142 __in DWORD dwStoreLocation
143 )
144{
145 //AssertSz(FALSE, "Debug ExecuteCertificateOperation() here.");
146 Assert(saAction & SCA_ACTION_INSTALL || saAction & SCA_ACTION_UNINSTALL);
147
148 HRESULT hr = S_OK;
149 LPWSTR pwzCaData = NULL;
150 LPWSTR pwz;
151 LPWSTR pwzName = NULL;
152 LPWSTR pwzStore = NULL;
153 int iAttributes = 0;
154 LPWSTR pwzPFXPassword = NULL;
155 LPWSTR pwzFilePath = NULL;
156 BYTE* pbData = NULL;
157 DWORD cbData = 0;
158 DWORD_PTR cbPFXPassword = 0;
159
160 BOOL fUserStoreLocation = (CERT_SYSTEM_STORE_CURRENT_USER == dwStoreLocation);
161 HCERTSTORE hCertStore = NULL;
162
163 hr = WcaGetProperty(L"CustomActionData", &pwzCaData);
164 ExitOnFailure(hr, "Failed to get CustomActionData");
165
166 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCaData);
167
168 pwz = pwzCaData;
169 hr = WcaReadStringFromCaData(&pwz, &pwzName);
170 ExitOnFailure(hr, "Failed to parse certificate name.");
171 hr = WcaReadStringFromCaData(&pwz, &pwzStore);
172 ExitOnFailure(hr, "Failed to parse CustomActionData, StoreName");
173 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
174 ExitOnFailure(hr, "Failed to parse certificate attribute");
175 if (SCA_ACTION_INSTALL == saAction) // install operations need more data
176 {
177 hr = WcaReadStreamFromCaData(&pwz, &pbData, (DWORD_PTR*)&cbData);
178 ExitOnFailure(hr, "Failed to parse certificate stream.");
179
180 hr = WcaReadStringFromCaData(&pwz, &pwzPFXPassword);
181 ExitOnFailure(hr, "Failed to parse certificate password.");
182 }
183
184 // Open the right store.
185 hCertStore = ::CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, dwStoreLocation | CERT_STORE_MAXIMUM_ALLOWED_FLAG, pwzStore);
186 MessageExitOnNullWithLastError(hCertStore, hr, msierrCERTFailedOpen, "Failed to open certificate store: %ls", pwzStore);
187
188 if (SCA_ACTION_INSTALL == saAction) // install operations need more data
189 {
190 // Uninstall existing versions of this package. Ignore any failures
191 // This is needed to clean up the private key of a cert when we replace an existing cert
192 // CertAddCertificateContextToStore(CERT_STORE_ADD_REPLACE_EXISTING) does not remove the private key if the cert is replaced
193 UninstallCertificatePackage(hCertStore, fUserStoreLocation, pwzName);
194
195 hr = InstallCertificatePackage(hCertStore, fUserStoreLocation, pwzName, pbData, cbData, iAttributes & SCA_CERT_ATTRIBUTE_VITAL, pwzPFXPassword);
196 ExitOnFailure(hr, "Failed to install certificate.");
197 }
198 else
199 {
200 Assert(SCA_ACTION_UNINSTALL == saAction);
201
202 hr = UninstallCertificatePackage(hCertStore, fUserStoreLocation, pwzName);
203 ExitOnFailure(hr, "Failed to uninstall certificate.");
204 }
205
206LExit:
207 if (NULL != pwzPFXPassword && SUCCEEDED(StrSize(pwzPFXPassword, &cbPFXPassword)))
208 {
209 SecureZeroMemory(pwzPFXPassword, cbPFXPassword);
210 }
211
212 if (hCertStore)
213 {
214 if (!::CertCloseStore(hCertStore, CERT_CLOSE_STORE_CHECK_FLAG))
215 {
216 WcaLog(LOGMSG_VERBOSE, "Cert store was closed but not all resources were freed. Error 0x%x", GetLastError());
217 }
218 }
219
220 ReleaseMem(pbData);
221 ReleaseStr(pwzFilePath);
222 ReleaseStr(pwzPFXPassword);
223 ReleaseStr(pwzStore);
224 ReleaseStr(pwzName);
225 ReleaseStr(pwzCaData);
226 return hr;
227}
228
229
230static HRESULT InstallCertificatePackage(
231 __in HCERTSTORE hStore,
232 __in BOOL fUserCertificateStore,
233 __in LPCWSTR wzName,
234 __in_opt BYTE* rgbData,
235 __in DWORD cbData,
236 __in BOOL fVital,
237 __in_opt LPCWSTR wzPFXPassword
238 )
239{
240 HRESULT hr = S_OK;
241
242 HCERTSTORE hPfxCertStore = NULL;
243 PCCERT_CONTEXT pCertContext = NULL;
244 CERT_BLOB blob = { 0 };
245 DWORD dwKeyset = fUserCertificateStore ? CRYPT_USER_KEYSET : CRYPT_MACHINE_KEYSET;
246 DWORD dwEncodingType;
247 DWORD dwContentType;
248 DWORD dwFormatType;
249 LPWSTR pwzUniqueName = NULL;
250 int iUniqueId = 0;
251
252 // Figure out what type of blob (certificate or PFX) we're dealing with here.
253 blob.pbData = rgbData;
254 blob.cbData = cbData;
255
256 if (!::CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, 0, &dwEncodingType, &dwContentType, &dwFormatType, NULL, NULL, (LPCVOID*)&pCertContext))
257 {
258 ExitWithLastError(hr, "Failed to parse the certificate blob: %ls", wzName);
259 }
260
261 hr = StrAllocFormatted(&pwzUniqueName, L"%s_wixCert_%d", wzName, ++iUniqueId);
262 ExitOnFailure(hr, "Failed to format unique name");
263
264 if (!pCertContext)
265 {
266 // If we have a PFX blob, get the first certificate out of the PFX and use that instead of the PFX.
267 if (dwContentType & CERT_QUERY_CONTENT_PFX)
268 {
269 ExitOnNull(wzPFXPassword, hr, E_INVALIDARG, "Failed to import PFX blob because no password was provided");
270
271 // If we fail and our password is blank, also try passing in NULL for the password (according to the docs)
272 hPfxCertStore = ::PFXImportCertStore((CRYPT_DATA_BLOB*)&blob, wzPFXPassword, dwKeyset);
273 if (NULL == hPfxCertStore && !*wzPFXPassword)
274 {
275 hPfxCertStore = ::PFXImportCertStore((CRYPT_DATA_BLOB*)&blob, NULL, dwKeyset);
276 }
277 ExitOnNullWithLastError(hPfxCertStore, hr, "Failed to open PFX file.");
278
279 // Install all certificates in the PFX
280 for (pCertContext = ::CertEnumCertificatesInStore(hPfxCertStore, pCertContext);
281 pCertContext;
282 pCertContext = ::CertEnumCertificatesInStore(hPfxCertStore, pCertContext))
283 {
284 hr = AddCertificate(hStore, pCertContext, pwzUniqueName, fVital);
285 MessageExitOnFailure(hr, msierrCERTFailedAdd, "Failed to add certificate to the store.");
286
287 hr = StrAllocFormatted(&pwzUniqueName, L"%s_wixCert_%d", wzName, ++iUniqueId);
288 ExitOnFailure(hr, "Failed to format unique name");
289 }
290 }
291 else
292 {
293 hr = E_UNEXPECTED;
294 ExitOnFailure(hr, "Unexpected certificate type processed.");
295 }
296 }
297 else
298 {
299 hr = AddCertificate(hStore, pCertContext, pwzUniqueName, fVital);
300 MessageExitOnFailure(hr, msierrCERTFailedAdd, "Failed to add certificate to the store.");
301 }
302
303 hr = WcaProgressMessage(COST_CERT_ADD, FALSE);
304 ExitOnFailure(hr, "Failed to send install progress message.");
305
306LExit:
307 ReleaseStr(pwzUniqueName);
308
309 if (pCertContext)
310 {
311 ::CertFreeCertificateContext(pCertContext);
312 }
313
314 // Close the stores after the context's are released.
315 if (hPfxCertStore)
316 {
317 if (!::CertCloseStore(hPfxCertStore, CERT_CLOSE_STORE_CHECK_FLAG))
318 {
319 WcaLog(LOGMSG_VERBOSE, "PFX cert store was closed but not all resources were freed. Error 0x%x", GetLastError());
320 }
321 }
322
323 return hr;
324}
325
326
327static HRESULT UninstallCertificatePackage(
328 __in HCERTSTORE hStore,
329 __in BOOL fUserCertificateStore,
330 __in LPCWSTR wzName
331 )
332{
333 HRESULT hr = S_OK;
334 DWORD er = ERROR_SUCCESS;
335 PCCERT_CONTEXT pCertContext = NULL;
336 CRYPT_KEY_PROV_INFO* pPrivateKeyInfo = NULL;
337 LPWSTR pwzUniquePrefix = NULL;
338 int ccUniquePrefix = 0;
339
340 hr = StrAllocFormatted(&pwzUniquePrefix, L"%s_wixCert_", wzName);
341 ExitOnFailure(hr, "Failed to format unique name");
342 ccUniquePrefix = ::lstrlenW(pwzUniquePrefix);
343
344 WcaLog(LOGMSG_STANDARD, "Deleting certificate that begin with friendly name: %ls", pwzUniquePrefix);
345
346 // Loop through all certificates in the store, deleting the ones that begin with our prefix.
347 while (NULL != (pCertContext = ::CertFindCertificateInStore(hStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, pCertContext)))
348 {
349 WCHAR wzFriendlyName[256] = { 0 };
350 DWORD cbFriendlyName = sizeof(wzFriendlyName);
351
352 if (::CertGetCertificateContextProperty(pCertContext, CERT_FRIENDLY_NAME_PROP_ID, reinterpret_cast<BYTE*>(wzFriendlyName), &cbFriendlyName) &&
353 lstrlenW(wzFriendlyName) >= ccUniquePrefix &&
354 CSTR_EQUAL == ::CompareStringW(LOCALE_SYSTEM_DEFAULT, 0, pwzUniquePrefix, ccUniquePrefix, wzFriendlyName, ccUniquePrefix))
355 {
356 PCCERT_CONTEXT pCertContextDelete = ::CertDuplicateCertificateContext(pCertContext); // duplicate the context so we can delete it with out disrupting the looping
357 if(pCertContextDelete)
358 {
359 // Delete the certificate and if successful delete the matching private key as well.
360 if (::CertDeleteCertificateFromStore(pCertContextDelete))
361 {
362 // If we found private key info, delete it.
363 hr = CertReadProperty(pCertContextDelete, CERT_KEY_PROV_INFO_PROP_ID, &pPrivateKeyInfo, NULL);
364 if (SUCCEEDED(hr))
365 {
366 HCRYPTPROV hProvIgnored = NULL; // ignored on deletes.
367 DWORD dwKeyset = fUserCertificateStore ? CRYPT_USER_KEYSET : CRYPT_MACHINE_KEYSET;
368
369 if (!::CryptAcquireContextW(&hProvIgnored, pPrivateKeyInfo->pwszContainerName, pPrivateKeyInfo->pwszProvName, pPrivateKeyInfo->dwProvType, dwKeyset | CRYPT_DELETEKEYSET | CRYPT_SILENT))
370 {
371 er = ::GetLastError();
372 hr = HRESULT_FROM_WIN32(er);
373 }
374
375 ReleaseNullMem(pPrivateKeyInfo);
376 }
377 else // don't worry about failures to delete private keys.
378 {
379 hr = S_OK;
380 }
381 }
382 else
383 {
384 er = ::GetLastError();
385 hr = HRESULT_FROM_WIN32(er);
386 }
387
388 if (FAILED(hr))
389 {
390 WcaLog(LOGMSG_STANDARD, "Failed to delete certificate with friendly name: %ls, continuing anyway. Error: 0x%x", wzFriendlyName, hr);
391 }
392
393 pCertContextDelete = NULL;
394 }
395 }
396 }
397
398 hr = WcaProgressMessage(COST_CERT_DELETE, FALSE);
399 ExitOnFailure(hr, "Failed to send uninstall progress message.");
400
401LExit:
402 ReleaseStr(pwzUniquePrefix);
403 ReleaseMem(pPrivateKeyInfo);
404 if(pCertContext)
405 {
406 ::CertFreeCertificateContext(pCertContext);
407 }
408
409 return hr;
410}
411
412static HRESULT AddCertificate(
413 __in HCERTSTORE hStore,
414 __in PCCERT_CONTEXT pCertContext,
415 __in LPCWSTR wzCertificateUniqueName,
416 __in BOOL fVital
417)
418{
419 HRESULT hr = S_OK;
420
421 WcaLog(LOGMSG_STANDARD, "Adding certificate: %ls", wzCertificateUniqueName);
422
423 hr = CertInstallSingleCertificate(hStore, pCertContext, wzCertificateUniqueName);
424 if (FAILED(hr) && !fVital)
425 {
426 WcaLog(LOGMSG_STANDARD, "Could not add non-vital certificate: %ls due to error: 0x%x, continuing...", wzCertificateUniqueName, hr);
427 hr = S_FALSE;
428 }
429
430 return hr;
431}