diff options
Diffstat (limited to 'src/ext/Iis/ca/scacertexec.cpp')
-rw-r--r-- | src/ext/Iis/ca/scacertexec.cpp | 431 |
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 | ||
8 | static HRESULT ExecuteCertificateOperation( | ||
9 | __in MSIHANDLE hInstall, | ||
10 | __in SCA_ACTION saAction, | ||
11 | __in DWORD dwStoreRoot | ||
12 | ); | ||
13 | |||
14 | static HRESULT ReadCertificateFile( | ||
15 | __in LPCWSTR wzPath, | ||
16 | __out BYTE** prgbData, | ||
17 | __out DWORD* pcbData | ||
18 | ); | ||
19 | |||
20 | static 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 | |||
30 | static HRESULT UninstallCertificatePackage( | ||
31 | __in HCERTSTORE hStore, | ||
32 | __in BOOL fUserCertificateStore, | ||
33 | __in LPCWSTR wzName | ||
34 | ); | ||
35 | |||
36 | static 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 | * ***************************************************************/ | ||
48 | extern "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 | |||
61 | LExit: | ||
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 | * ***************************************************************/ | ||
72 | extern "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 | |||
85 | LExit: | ||
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 | * ***************************************************************/ | ||
96 | extern "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 | |||
109 | LExit: | ||
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 | * ***************************************************************/ | ||
120 | extern "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 | |||
133 | LExit: | ||
134 | er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; | ||
135 | return WcaFinalize(er); | ||
136 | } | ||
137 | |||
138 | |||
139 | static 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 | |||
206 | LExit: | ||
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 | |||
230 | static 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 | |||
306 | LExit: | ||
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 | |||
327 | static 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 | |||
401 | LExit: | ||
402 | ReleaseStr(pwzUniquePrefix); | ||
403 | ReleaseMem(pPrivateKeyInfo); | ||
404 | if(pCertContext) | ||
405 | { | ||
406 | ::CertFreeCertificateContext(pCertContext); | ||
407 | } | ||
408 | |||
409 | return hr; | ||
410 | } | ||
411 | |||
412 | static 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 | } | ||