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