diff options
Diffstat (limited to 'src/ext/Iis/ca/scacert.cpp')
-rw-r--r-- | src/ext/Iis/ca/scacert.cpp | 1482 |
1 files changed, 1482 insertions, 0 deletions
diff --git a/src/ext/Iis/ca/scacert.cpp b/src/ext/Iis/ca/scacert.cpp new file mode 100644 index 00000000..5eae905a --- /dev/null +++ b/src/ext/Iis/ca/scacert.cpp | |||
@@ -0,0 +1,1482 @@ | |||
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 | // prototypes | ||
6 | static HRESULT ConfigureCertificates( | ||
7 | __in SCA_ACTION saAction | ||
8 | ); | ||
9 | |||
10 | static LPCWSTR StoreMapping( | ||
11 | __in int iStore | ||
12 | ); | ||
13 | |||
14 | static HRESULT FindExistingCertificate( | ||
15 | __in LPCWSTR wzName, | ||
16 | __in DWORD dwStoreLocation, | ||
17 | __in LPCWSTR wzStore, | ||
18 | __out BYTE** prgbCertificate, | ||
19 | __out DWORD* pcbCertificate | ||
20 | ); | ||
21 | |||
22 | static HRESULT ResolveCertificate( | ||
23 | __in LPCWSTR wzId, | ||
24 | __in LPCWSTR wzName, | ||
25 | __in DWORD dwStoreLocation, | ||
26 | __in LPCWSTR wzStoreName, | ||
27 | __in DWORD dwAttributess, | ||
28 | __in LPCWSTR wzData, | ||
29 | __in LPCWSTR wzPFXPassword, | ||
30 | __out BYTE** ppbCertificate, | ||
31 | __out DWORD* pcbCertificate | ||
32 | ); | ||
33 | |||
34 | static HRESULT ReadCertificateFile( | ||
35 | __in LPCWSTR wzPath, | ||
36 | __out BYTE** prgbData, | ||
37 | __out DWORD* pcbData | ||
38 | ); | ||
39 | |||
40 | static HRESULT CertificateToHash( | ||
41 | __in BYTE* pbCertificate, | ||
42 | __in DWORD cbCertificate, | ||
43 | __in DWORD dwStoreLocation, | ||
44 | __in LPCWSTR wzPFXPassword, | ||
45 | __in BYTE rgbHash[], | ||
46 | __in DWORD cbHash | ||
47 | ); | ||
48 | |||
49 | /* | ||
50 | HRESULT ScaGetCertificateByPath(LPCWSTR pwzName, BOOL fIsInstalling, | ||
51 | BOOL fIsUninstalling, INT iStore, | ||
52 | INT iStoreLocation, LPCWSTR wzSslCertificate, | ||
53 | LPCWSTR wzPFXPassword, BSTR* pbstrCertificate, | ||
54 | DWORD* pcbCertificate, BYTE* pbaHashBuffer); | ||
55 | |||
56 | HRESULT ScaGetCertificateByRequest(LPCWSTR pwzName, BOOL fIsInstalling, | ||
57 | BOOL fIsUninstalling, INT iStore, | ||
58 | INT iStoreLocation, LPCWSTR wzDistinguishedName, | ||
59 | LPCWSTR wzCA, BSTR* pbstrCertificate, | ||
60 | DWORD* pcbCertificate, BYTE* pbaHashBuffer); | ||
61 | |||
62 | HRESULT ScaSslNewCertificate(LPCWSTR pwzName, INT iStore, | ||
63 | INT iStoreLocation, LPCWSTR wzComputerName, | ||
64 | LPCWSTR wzDistinguishedName, LPCWSTR wzCertificateAuthorityOrig, | ||
65 | BSTR* pbstrCertificate, DWORD* pcbCertificate, | ||
66 | BYTE* pbaHashBuffer); | ||
67 | |||
68 | HRESULT ScaSslExistingCertificateByName(LPCWSTR pwzName, INT iStore, | ||
69 | INT iStoreLocation, BSTR* pbstrCertificate, | ||
70 | DWORD* pcbCertificate, BYTE* pbaHashBuffer); | ||
71 | |||
72 | HRESULT ScaSslExistingCertificateByBinaryData(INT iStore, INT iStoreLocation, | ||
73 | BYTE* pwzData, DWORD cchData); | ||
74 | |||
75 | HRESULT CreateEnroll(ICEnroll2 **hEnroll, INT iStore, | ||
76 | INT iStoreLocation); | ||
77 | |||
78 | HRESULT RequestCertificate(LPCWSTR pwzName, INT iStore, | ||
79 | INT iStoreLocation, LPCWSTR wzComputerName, | ||
80 | LPCWSTR wzDistinguishedName, LPCWSTR wzCertificateAuthority, | ||
81 | BSTR *pbstrCertificate); | ||
82 | |||
83 | VOID ParseCertificateAuthority(__in LPCWSTR wzCertificateAuthorityOrig, __out LPWSTR *pwzBuffer, | ||
84 | __out LPWSTR **hwzCAArray, __out int *piCAArray); | ||
85 | */ | ||
86 | |||
87 | |||
88 | LPCWSTR vcsCertQuery = L"SELECT `Certificate`, `Name`, `Component_`, `StoreLocation`, `StoreName`, `Attributes`, `Binary_`, `CertificatePath`, `PFXPassword` FROM `Certificate`"; | ||
89 | enum eCertQuery { cqCertificate = 1, cqName, cqComponent, cqStoreLocation, cqStoreName, cqAttributes, cqCertificateBinary, cqCertificatePath, cqPFXPassword }; | ||
90 | |||
91 | |||
92 | /******************************************************************** | ||
93 | InstallCertificates - CUSTOM ACTION ENTRY POINT for installing | ||
94 | certificates | ||
95 | |||
96 | ********************************************************************/ | ||
97 | extern "C" UINT __stdcall InstallCertificates( | ||
98 | __in MSIHANDLE hInstall | ||
99 | ) | ||
100 | { | ||
101 | HRESULT hr = S_OK; | ||
102 | UINT er = ERROR_SUCCESS; | ||
103 | |||
104 | // initialize | ||
105 | hr = WcaInitialize(hInstall, "InstallCertificates"); | ||
106 | ExitOnFailure(hr, "Failed to initialize"); | ||
107 | |||
108 | hr = ConfigureCertificates(SCA_ACTION_INSTALL); | ||
109 | |||
110 | LExit: | ||
111 | er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; | ||
112 | return WcaFinalize(er); | ||
113 | } | ||
114 | |||
115 | |||
116 | /******************************************************************** | ||
117 | UninstallCertificates - CUSTOM ACTION ENTRY POINT for uninstalling | ||
118 | certificates | ||
119 | |||
120 | ********************************************************************/ | ||
121 | extern "C" UINT __stdcall UninstallCertificates( | ||
122 | __in MSIHANDLE hInstall | ||
123 | ) | ||
124 | { | ||
125 | HRESULT hr = S_OK; | ||
126 | UINT er = ERROR_SUCCESS; | ||
127 | |||
128 | // initialize | ||
129 | hr = WcaInitialize(hInstall, "UninstallCertificates"); | ||
130 | ExitOnFailure(hr, "Failed to initialize"); | ||
131 | |||
132 | hr = ConfigureCertificates(SCA_ACTION_UNINSTALL); | ||
133 | |||
134 | LExit: | ||
135 | er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; | ||
136 | return WcaFinalize(er); | ||
137 | } | ||
138 | |||
139 | |||
140 | static HRESULT ConfigureCertificates( | ||
141 | __in SCA_ACTION saAction | ||
142 | ) | ||
143 | { | ||
144 | //AssertSz(FALSE, "debug ConfigureCertificates()."); | ||
145 | |||
146 | HRESULT hr = S_OK; | ||
147 | DWORD er = ERROR_SUCCESS; | ||
148 | |||
149 | PMSIHANDLE hViewCertificate; | ||
150 | PMSIHANDLE hRecCertificate; | ||
151 | INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN; | ||
152 | INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN; | ||
153 | |||
154 | WCHAR* pwzId = NULL; | ||
155 | WCHAR* pwzName = NULL; | ||
156 | WCHAR* pwzComponent = NULL; | ||
157 | int iData = 0; | ||
158 | DWORD dwStoreLocation = 0; | ||
159 | LPWSTR pwzStoreName = 0; | ||
160 | DWORD dwAttributes = 0; | ||
161 | WCHAR* pwzData = NULL; | ||
162 | WCHAR* pwzPFXPassword = NULL; | ||
163 | WCHAR* pwzCaData = NULL; | ||
164 | WCHAR* pwzRollbackCaData = NULL; | ||
165 | |||
166 | BYTE* pbCertificate = NULL; | ||
167 | DWORD cbCertificate = 0; | ||
168 | DWORD_PTR cbPFXPassword = 0; | ||
169 | |||
170 | // Bail quickly if the Certificate table isn't around. | ||
171 | if (S_OK != WcaTableExists(L"Certificate")) | ||
172 | { | ||
173 | WcaLog(LOGMSG_VERBOSE, "Skipping ConfigureCertificates() - required table not present."); | ||
174 | ExitFunction1(hr = S_FALSE); | ||
175 | } | ||
176 | |||
177 | // Process the Certificate table. | ||
178 | hr = WcaOpenExecuteView(vcsCertQuery, &hViewCertificate); | ||
179 | ExitOnFailure(hr, "failed to open view on Certificate table"); | ||
180 | |||
181 | while (SUCCEEDED(hr = WcaFetchRecord(hViewCertificate, &hRecCertificate))) | ||
182 | { | ||
183 | hr = WcaGetRecordString(hRecCertificate, cqCertificate, &pwzId); // the id is just useful to have up front | ||
184 | ExitOnFailure(hr, "failed to get Certificate.Certificate"); | ||
185 | |||
186 | hr = WcaGetRecordString(hRecCertificate, cqComponent, &pwzComponent); | ||
187 | ExitOnFailure(hr, "failed to get Certificate.Component_"); | ||
188 | |||
189 | er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzComponent, &isInstalled, &isAction); | ||
190 | hr = HRESULT_FROM_WIN32(er); | ||
191 | ExitOnFailure(hr, "failed to get state for component: %ls", pwzComponent); | ||
192 | |||
193 | if (!(WcaIsInstalling(isInstalled, isAction) && SCA_ACTION_INSTALL == saAction) && | ||
194 | !(WcaIsUninstalling(isInstalled, isAction) && SCA_ACTION_UNINSTALL == saAction) && | ||
195 | !(WcaIsReInstalling(isInstalled, isAction))) | ||
196 | { | ||
197 | WcaLog(LOGMSG_VERBOSE, "Skipping non-action certificate: %ls", pwzId); | ||
198 | continue; | ||
199 | } | ||
200 | |||
201 | // extract the rest of the data from the Certificate table | ||
202 | hr = WcaGetRecordFormattedString(hRecCertificate, cqName, &pwzName); | ||
203 | ExitOnFailure(hr, "failed to get Certificate.Name"); | ||
204 | |||
205 | hr = WcaGetRecordInteger(hRecCertificate, cqStoreLocation, &iData); | ||
206 | ExitOnFailure(hr, "failed to get Certificate.StoreLocation"); | ||
207 | |||
208 | switch (iData) | ||
209 | { | ||
210 | case SCA_CERTSYSTEMSTORE_CURRENTUSER: | ||
211 | dwStoreLocation = CERT_SYSTEM_STORE_CURRENT_USER; | ||
212 | break; | ||
213 | case SCA_CERTSYSTEMSTORE_LOCALMACHINE: | ||
214 | dwStoreLocation = CERT_SYSTEM_STORE_LOCAL_MACHINE; | ||
215 | break; | ||
216 | default: | ||
217 | hr = E_INVALIDARG; | ||
218 | ExitOnFailure(hr, "Invalid store location value: %d", iData); | ||
219 | } | ||
220 | |||
221 | hr = WcaGetRecordString(hRecCertificate, cqStoreName, &pwzStoreName); | ||
222 | ExitOnFailure(hr, "failed to get Certificate.StoreName"); | ||
223 | |||
224 | hr = WcaGetRecordInteger(hRecCertificate, cqAttributes, reinterpret_cast<int*>(&dwAttributes)); | ||
225 | ExitOnFailure(hr, "failed to get Certificate.Attributes"); | ||
226 | |||
227 | if (dwAttributes & SCA_CERT_ATTRIBUTE_BINARYDATA) | ||
228 | { | ||
229 | hr = WcaGetRecordString(hRecCertificate, cqCertificateBinary, &pwzData); | ||
230 | ExitOnFailure(hr, "failed to get Certificate.Binary_"); | ||
231 | } | ||
232 | else | ||
233 | { | ||
234 | hr = WcaGetRecordFormattedString(hRecCertificate, cqCertificatePath, &pwzData); | ||
235 | ExitOnFailure(hr, "failed to get Certificate.CertificatePath"); | ||
236 | } | ||
237 | |||
238 | hr = WcaGetRecordFormattedString(hRecCertificate, cqPFXPassword, &pwzPFXPassword); | ||
239 | ExitOnFailure(hr, "failed to get Certificate.PFXPassword"); | ||
240 | |||
241 | // Write the common data (for both install and uninstall) to the CustomActionData | ||
242 | // to pass data to the deferred CustomAction. | ||
243 | hr = StrAllocString(&pwzCaData, pwzName, 0); | ||
244 | ExitOnFailure(hr, "Failed to pass Certificate.Certificate to deferred CustomAction."); | ||
245 | hr = WcaWriteStringToCaData(pwzStoreName, &pwzCaData); | ||
246 | ExitOnFailure(hr, "Failed to pass Certificate.StoreName to deferred CustomAction."); | ||
247 | hr = WcaWriteIntegerToCaData(dwAttributes, &pwzCaData); | ||
248 | ExitOnFailure(hr, "Failed to pass Certificate.Attributes to deferred CustomAction."); | ||
249 | |||
250 | // Copy the rollback data from the deferred data because it's the same up to this point. | ||
251 | hr = StrAllocString(&pwzRollbackCaData, pwzCaData, 0); | ||
252 | ExitOnFailure(hr, "Failed to allocate string for rollback CustomAction."); | ||
253 | |||
254 | // Finally, schedule the correct deferred CustomAction to actually do work. | ||
255 | LPCWSTR wzAction = NULL; | ||
256 | LPCWSTR wzRollbackAction = NULL; | ||
257 | DWORD dwCost = 0; | ||
258 | if (SCA_ACTION_UNINSTALL == saAction) | ||
259 | { | ||
260 | // Find an existing certificate one (if there is one) to so we have it for rollback. | ||
261 | hr = FindExistingCertificate(pwzName, dwStoreLocation, pwzStoreName, &pbCertificate, &cbCertificate); | ||
262 | ExitOnFailure(hr, "Failed to search for existing certificate with friendly name: %ls", pwzName); | ||
263 | |||
264 | if (pbCertificate) | ||
265 | { | ||
266 | hr = WcaWriteStreamToCaData(pbCertificate, cbCertificate, &pwzRollbackCaData); | ||
267 | ExitOnFailure(hr, "Failed to pass Certificate.Data to rollback CustomAction."); | ||
268 | |||
269 | hr = WcaWriteStringToCaData(pwzPFXPassword, &pwzRollbackCaData); | ||
270 | ExitOnFailure(hr, "Failed to pass Certificate.PFXPassword to rollback CustomAction."); | ||
271 | |||
272 | hr = WcaWriteIntegerToCaData(dwAttributes, &pwzCaData); | ||
273 | ExitOnFailure(hr, "Failed to pass Certificate.Attributes to deferred CustomAction."); | ||
274 | } | ||
275 | |||
276 | // Pick the right action to run based on what store we're uninstalling from. | ||
277 | if (CERT_SYSTEM_STORE_LOCAL_MACHINE == dwStoreLocation) | ||
278 | { | ||
279 | wzAction = CUSTOM_ACTION_DECORATION(L"DeleteMachineCertificate"); | ||
280 | if (pbCertificate) | ||
281 | { | ||
282 | wzRollbackAction = L"RollbackDeleteMachineCertificate"; | ||
283 | } | ||
284 | } | ||
285 | else | ||
286 | { | ||
287 | wzAction = CUSTOM_ACTION_DECORATION(L"DeleteUserCertificate"); | ||
288 | if (pbCertificate) | ||
289 | { | ||
290 | wzRollbackAction = L"RollbackDeleteUserCertificate"; | ||
291 | } | ||
292 | } | ||
293 | dwCost = COST_CERT_DELETE; | ||
294 | } | ||
295 | else | ||
296 | { | ||
297 | // Actually get the certificate, resolve it to a blob, and get the blob's hash. | ||
298 | hr = ResolveCertificate(pwzId, pwzName, dwStoreLocation, pwzStoreName, dwAttributes, pwzData, pwzPFXPassword, &pbCertificate, &cbCertificate); | ||
299 | ExitOnFailure(hr, "Failed to resolve certificate: %ls", pwzId); | ||
300 | |||
301 | hr = WcaWriteStreamToCaData(pbCertificate, cbCertificate, &pwzCaData); | ||
302 | ExitOnFailure(hr, "Failed to pass Certificate.Data to deferred CustomAction."); | ||
303 | |||
304 | hr = WcaWriteStringToCaData(pwzPFXPassword, &pwzCaData); | ||
305 | ExitOnFailure(hr, "Failed to pass Certificate.PFXPassword to deferred CustomAction."); | ||
306 | |||
307 | // Pick the right action to run based on what store we're installing into. | ||
308 | if (CERT_SYSTEM_STORE_LOCAL_MACHINE == dwStoreLocation) | ||
309 | { | ||
310 | wzAction = CUSTOM_ACTION_DECORATION(L"AddMachineCertificate"); | ||
311 | wzRollbackAction = CUSTOM_ACTION_DECORATION(L"RollbackAddMachineCertificate"); | ||
312 | } | ||
313 | else | ||
314 | { | ||
315 | wzAction = CUSTOM_ACTION_DECORATION(L"AddUserCertificate"); | ||
316 | wzRollbackAction = CUSTOM_ACTION_DECORATION(L"RollbackAddUserCertificate"); | ||
317 | } | ||
318 | dwCost = COST_CERT_ADD; | ||
319 | } | ||
320 | |||
321 | if (wzRollbackAction) | ||
322 | { | ||
323 | hr = WcaDoDeferredAction(wzRollbackAction, pwzRollbackCaData, dwCost); | ||
324 | ExitOnFailure(hr, "Failed to schedule rollback certificate action '%ls' for: %ls", wzRollbackAction, pwzId); | ||
325 | } | ||
326 | |||
327 | hr = WcaDoDeferredAction(wzAction, pwzCaData, dwCost); | ||
328 | ExitOnFailure(hr, "Failed to schedule certificate action '%ls' for: %ls", wzAction, pwzId); | ||
329 | |||
330 | // Clean up for the next certificate. | ||
331 | ReleaseNullMem(pbCertificate); | ||
332 | } | ||
333 | |||
334 | if (E_NOMOREITEMS == hr) | ||
335 | { | ||
336 | hr = S_OK; | ||
337 | } | ||
338 | |||
339 | LExit: | ||
340 | if (NULL != pwzPFXPassword && SUCCEEDED(StrSize(pwzPFXPassword, &cbPFXPassword))) | ||
341 | { | ||
342 | SecureZeroMemory(pwzPFXPassword, cbPFXPassword); | ||
343 | } | ||
344 | |||
345 | ReleaseMem(pbCertificate); | ||
346 | ReleaseStr(pwzCaData); | ||
347 | ReleaseStr(pwzPFXPassword); | ||
348 | ReleaseStr(pwzData); | ||
349 | ReleaseStr(pwzName); | ||
350 | ReleaseStr(pwzStoreName); | ||
351 | ReleaseStr(pwzComponent); | ||
352 | ReleaseStr(pwzId); | ||
353 | |||
354 | return hr; | ||
355 | } | ||
356 | |||
357 | |||
358 | static HRESULT ResolveCertificate( | ||
359 | __in LPCWSTR wzId, | ||
360 | __in LPCWSTR /*wzName*/, | ||
361 | __in DWORD dwStoreLocation, | ||
362 | __in LPCWSTR /*wzStoreName*/, | ||
363 | __in DWORD dwAttributes, | ||
364 | __in LPCWSTR wzData, | ||
365 | __in LPCWSTR wzPFXPassword, | ||
366 | __out BYTE** ppbCertificate, | ||
367 | __out DWORD* pcbCertificate | ||
368 | ) | ||
369 | { | ||
370 | HRESULT hr = S_OK; | ||
371 | |||
372 | LPWSTR pwzSql = NULL; | ||
373 | PMSIHANDLE hView; | ||
374 | PMSIHANDLE hRec; | ||
375 | MSIHANDLE hCertificateHashView = NULL; | ||
376 | MSIHANDLE hCertificateHashColumns = NULL; | ||
377 | |||
378 | BYTE rgbCertificateHash[CB_CERTIFICATE_HASH] = { 0 }; | ||
379 | WCHAR wzEncodedCertificateHash[CB_CERTIFICATE_HASH * 2 + 1] = { 0 }; | ||
380 | |||
381 | PMSIHANDLE hViewCertificateRequest, hRecCertificateRequest; | ||
382 | |||
383 | WCHAR* pwzDistinguishedName = NULL; | ||
384 | WCHAR* pwzCA = NULL; | ||
385 | |||
386 | BYTE* pbData = NULL; | ||
387 | DWORD cbData = 0; | ||
388 | |||
389 | if (dwAttributes & SCA_CERT_ATTRIBUTE_REQUEST) | ||
390 | { | ||
391 | hr = E_NOTIMPL; | ||
392 | ExitOnFailure(hr, "Installing certificates by requesting them from a certificate authority is not currently supported"); | ||
393 | //if (dwAttributes & SCA_CERT_ATTRIBUTE_OVERWRITE) | ||
394 | //{ | ||
395 | // // try to overwrite with the patch to a cert file | ||
396 | // WcaLog(LOGMSG_VERBOSE, "ConfigureCertificates - Overwrite with SSLCERTIFICATE"); | ||
397 | // hr = ScaGetCertificateByPath(pwzName, fIsInstalling, fIsUninstalling, | ||
398 | // iStore, iStoreLocation, pwzData, wzPFXPassword, pbstrCertificate, pcbCertificate, pbaHashBuffer); | ||
399 | //} | ||
400 | //if (hr != S_OK) | ||
401 | //{ | ||
402 | // if (fIsUninstalling && !fIsInstalling) | ||
403 | // { | ||
404 | // // for uninstall, we just want to find the existing certificate | ||
405 | // hr = ScaSslExistingCertificateByName(pwzName, iStore, iStoreLocation, pbstrCertificate, pcbCertificate, pbaHashBuffer); | ||
406 | // ExitOnFailure(hr, "Failed Retrieving existing certificate during uninstall"); | ||
407 | // // ok if no existing cert | ||
408 | // if (S_OK != hr) | ||
409 | // hr = S_OK; | ||
410 | // } | ||
411 | // else | ||
412 | // { | ||
413 | // // still no certificate | ||
414 | // // user has request this certificate, try to locate DistinguishedName and CA | ||
415 | // hr = WcaTableExists(L"CertificateRequest"); | ||
416 | // ExitOnFailure(hr, "CertificateRequest is referenced but not found"); | ||
417 | // WcaLog(LOGMSG_VERBOSE, "ConfigureCertificates - CertificateRequest table present"); | ||
418 | // cchSQLView = 255 + lstrlenW(pwzName); | ||
419 | // pwzSQLView = new WCHAR[cchSQLView]; | ||
420 | // if (pwzSQLView) | ||
421 | // { | ||
422 | // hr = ::StringCchPrintfW(pwzSQLView, cchSQLView, L"SELECT `DistinguishedName`, `CA` FROM `CertificateRequest` WHERE `Certificate_`=\'%s\'", pwzName); | ||
423 | // ExitOnFailure(hr, "::StringCchPrintfW failed"); | ||
424 | // hr = WcaOpenExecuteView(pwzSQLView, &hViewCertificateRequest); | ||
425 | // ExitOnFailure(hr, "failed to open view on CertificateRequest table"); | ||
426 | // hr = WcaFetchSingleRecord(hViewCertificateRequest, &hRecCertificateRequest); | ||
427 | // ExitOnFailure(hr, "failed to retrieve request from CertificateRequest table"); | ||
428 | // hr = WcaGetRecordString(hRecCertificateRequest, 1, &pwzDistinguishedName); | ||
429 | // ExitOnFailure(hr, "failed to get DistinguishedName"); | ||
430 | // hr = WcaGetRecordString(hRecCertificateRequest, 2, &pwzCA); | ||
431 | // ExitOnFailure(hr, "failed to get CA"); | ||
432 | // if (pwzDistinguishedName && pwzCA && *pwzDistinguishedName && *pwzCA) | ||
433 | // { | ||
434 | // hr = ScaGetCertificateByRequest(pwzName, fIsInstalling, fIsUninstalling, iStore, iStoreLocation, pwzDistinguishedName, pwzCA, pbstrCertificate, pcbCertificate, pbaHashBuffer); | ||
435 | // } | ||
436 | // else | ||
437 | // { | ||
438 | // hr = E_FAIL; | ||
439 | // ExitOnFailure(hr, "CertificateRequest entry is empty"); | ||
440 | // } | ||
441 | // } | ||
442 | // else | ||
443 | // { | ||
444 | // hr = E_FAIL; | ||
445 | // ExitOnFailure(hr, "Out of memory"); | ||
446 | // } | ||
447 | // } | ||
448 | //} | ||
449 | } | ||
450 | else if (dwAttributes & SCA_CERT_ATTRIBUTE_BINARYDATA) | ||
451 | { | ||
452 | // get the binary stream in Binary | ||
453 | hr = WcaTableExists(L"Binary"); | ||
454 | if (S_OK != hr) | ||
455 | { | ||
456 | if (SUCCEEDED(hr)) | ||
457 | { | ||
458 | hr = E_UNEXPECTED; | ||
459 | } | ||
460 | ExitOnFailure(hr, "Binary was referenced but there is no Binary table."); | ||
461 | } | ||
462 | |||
463 | hr = StrAllocFormatted(&pwzSql, L"SELECT `Data` FROM `Binary` WHERE `Name`=\'%s\'", wzData); | ||
464 | ExitOnFailure(hr, "Failed to allocate Binary table query."); | ||
465 | |||
466 | hr = WcaOpenExecuteView(pwzSql, &hView); | ||
467 | ExitOnFailure(hr, "Failed to open view on Binary table"); | ||
468 | |||
469 | hr = WcaFetchSingleRecord(hView, &hRec); | ||
470 | ExitOnFailure(hr, "Failed to retrieve request from Binary table"); | ||
471 | |||
472 | hr = WcaGetRecordStream(hRec, 1, &pbData, &cbData); | ||
473 | ExitOnFailure(hr, "Failed to ready Binary.Data for certificate."); | ||
474 | } | ||
475 | else if (dwAttributes == SCA_CERT_ATTRIBUTE_DEFAULT) | ||
476 | { | ||
477 | hr = ReadCertificateFile(wzData, &pbData, &cbData); | ||
478 | ExitOnFailure(hr, "Failed to read certificate from file path."); | ||
479 | } | ||
480 | else | ||
481 | { | ||
482 | hr = E_INVALIDARG; | ||
483 | ExitOnFailure(hr, "Invalid Certificate.Attributes."); | ||
484 | } | ||
485 | |||
486 | // If we have loaded a certificate, update the Certificate.Hash column. | ||
487 | if (pbData) | ||
488 | { | ||
489 | hr = CertificateToHash(pbData, cbData, dwStoreLocation, wzPFXPassword, rgbCertificateHash, countof(rgbCertificateHash)); | ||
490 | ExitOnFailure(hr, "Failed to get SHA1 hash of certificate."); | ||
491 | |||
492 | hr = StrHexEncode(rgbCertificateHash, countof(rgbCertificateHash), wzEncodedCertificateHash, countof(wzEncodedCertificateHash)); | ||
493 | ExitOnFailure(hr, "Failed to hex encode SHA1 hash of certificate."); | ||
494 | |||
495 | // Update the CertificateHash table. | ||
496 | hr = WcaAddTempRecord(&hCertificateHashView, &hCertificateHashColumns, L"CertificateHash", NULL, 0, 2, wzId, wzEncodedCertificateHash); | ||
497 | ExitOnFailure(hr, "Failed to add encoded has for certificate: %ls", wzId); | ||
498 | } | ||
499 | |||
500 | *ppbCertificate = pbData; | ||
501 | *pcbCertificate = cbData; | ||
502 | pbData = NULL; | ||
503 | |||
504 | LExit: | ||
505 | if (hCertificateHashColumns) | ||
506 | { | ||
507 | ::MsiCloseHandle(hCertificateHashColumns); | ||
508 | } | ||
509 | |||
510 | if (hCertificateHashView) | ||
511 | { | ||
512 | ::MsiCloseHandle(hCertificateHashView); | ||
513 | } | ||
514 | |||
515 | ReleaseStr(pwzDistinguishedName); | ||
516 | ReleaseStr(pwzCA); | ||
517 | ReleaseMem(pbData); | ||
518 | ReleaseStr(pwzSql); | ||
519 | |||
520 | return hr; | ||
521 | } | ||
522 | |||
523 | |||
524 | static HRESULT ReadCertificateFile( | ||
525 | __in LPCWSTR wzPath, | ||
526 | __out BYTE** prgbData, | ||
527 | __out DWORD* pcbData | ||
528 | ) | ||
529 | { | ||
530 | HRESULT hr = S_OK; | ||
531 | |||
532 | PCCERT_CONTEXT pCertContext = NULL; | ||
533 | DWORD dwContentType; | ||
534 | BYTE* pbData = NULL; | ||
535 | DWORD cbData = 0; | ||
536 | |||
537 | if (!::CryptQueryObject(CERT_QUERY_OBJECT_FILE, reinterpret_cast<LPCVOID>(wzPath), CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, &dwContentType, NULL, NULL, NULL, (LPCVOID*)&pCertContext)) | ||
538 | { | ||
539 | ExitOnFailure(hr, "Failed to read certificate from file: %ls", wzPath); | ||
540 | } | ||
541 | |||
542 | if (pCertContext) | ||
543 | { | ||
544 | cbData = pCertContext->cbCertEncoded; | ||
545 | pbData = static_cast<BYTE*>(MemAlloc(cbData, FALSE)); | ||
546 | ExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to allocate memory to read certificate from file: %ls", wzPath); | ||
547 | |||
548 | CopyMemory(pbData, pCertContext->pbCertEncoded, pCertContext->cbCertEncoded); | ||
549 | } | ||
550 | else | ||
551 | { | ||
552 | // If we have a PFX blob, get the first certificate out of the PFX and use that instead of the PFX. | ||
553 | if (dwContentType & CERT_QUERY_CONTENT_PFX) | ||
554 | { | ||
555 | hr = FileRead(&pbData, &cbData, wzPath); | ||
556 | ExitOnFailure(hr, "Failed to read PFX file: %ls", wzPath); | ||
557 | } | ||
558 | else | ||
559 | { | ||
560 | hr = E_UNEXPECTED; | ||
561 | ExitOnFailure(hr, "Unexpected certificate type read from disk."); | ||
562 | } | ||
563 | } | ||
564 | |||
565 | *pcbData = cbData; | ||
566 | *prgbData = pbData; | ||
567 | pbData = NULL; | ||
568 | |||
569 | LExit: | ||
570 | ReleaseMem(pbData); | ||
571 | return hr; | ||
572 | } | ||
573 | |||
574 | |||
575 | static HRESULT CertificateToHash( | ||
576 | __in BYTE* pbCertificate, | ||
577 | __in DWORD cbCertificate, | ||
578 | __in DWORD dwStoreLocation, | ||
579 | __in LPCWSTR wzPFXPassword, | ||
580 | __in BYTE rgbHash[], | ||
581 | __in DWORD cbHash | ||
582 | ) | ||
583 | { | ||
584 | HRESULT hr = S_OK; | ||
585 | |||
586 | HCERTSTORE hPfxCertStore = NULL; | ||
587 | PCCERT_CONTEXT pCertContext = NULL; | ||
588 | PCCERT_CONTEXT pCertContextEnum = NULL; | ||
589 | CRYPT_DATA_BLOB blob = { 0 }; | ||
590 | CRYPT_KEY_PROV_INFO* pPfxInfo = NULL; | ||
591 | DWORD dwKeyset = (CERT_SYSTEM_STORE_CURRENT_USER == dwStoreLocation) ? CRYPT_USER_KEYSET : CRYPT_MACHINE_KEYSET; | ||
592 | DWORD dwEncodingType; | ||
593 | DWORD dwContentType; | ||
594 | DWORD dwFormatType; | ||
595 | |||
596 | blob.pbData = pbCertificate; | ||
597 | blob.cbData = cbCertificate; | ||
598 | |||
599 | 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)) | ||
600 | { | ||
601 | ExitWithLastError(hr, "Failed to process certificate as a valid certificate."); | ||
602 | } | ||
603 | |||
604 | if (!pCertContext) | ||
605 | { | ||
606 | // If we have a PFX blob, get the first certificate out of the PFX and use that instead of the PFX. | ||
607 | if (dwContentType & CERT_QUERY_CONTENT_PFX) | ||
608 | { | ||
609 | // If we fail and our password is blank, also try passing in NULL for the password (according to the docs) | ||
610 | hPfxCertStore = ::PFXImportCertStore((CRYPT_DATA_BLOB*)&blob, wzPFXPassword, dwKeyset); | ||
611 | if (NULL == hPfxCertStore && !*wzPFXPassword) | ||
612 | { | ||
613 | hPfxCertStore = ::PFXImportCertStore((CRYPT_DATA_BLOB*)&blob, NULL, dwKeyset); | ||
614 | } | ||
615 | ExitOnNullWithLastError(hPfxCertStore, hr, "Failed to open PFX file."); | ||
616 | |||
617 | // Find the first cert with a private key, or just use the last one | ||
618 | for (pCertContextEnum = ::CertEnumCertificatesInStore(hPfxCertStore, pCertContextEnum); | ||
619 | pCertContextEnum; | ||
620 | pCertContextEnum = ::CertEnumCertificatesInStore(hPfxCertStore, pCertContextEnum)) | ||
621 | { | ||
622 | pCertContext = pCertContextEnum; | ||
623 | |||
624 | if (pCertContext && CertHasPrivateKey(pCertContext, NULL)) | ||
625 | { | ||
626 | break; | ||
627 | } | ||
628 | } | ||
629 | |||
630 | ExitOnNullWithLastError(pCertContext, hr, "Failed to read first certificate out of PFX file."); | ||
631 | |||
632 | // Ignore failures, the worst that happens is some parts of the PFX get left behind. | ||
633 | CertReadProperty(pCertContext, CERT_KEY_PROV_INFO_PROP_ID, &pPfxInfo, NULL); | ||
634 | } | ||
635 | else | ||
636 | { | ||
637 | hr = E_UNEXPECTED; | ||
638 | ExitOnFailure(hr, "Unexpected certificate type processed."); | ||
639 | } | ||
640 | } | ||
641 | |||
642 | DWORD cb = cbHash; | ||
643 | if (!::CertGetCertificateContextProperty(pCertContext, CERT_SHA1_HASH_PROP_ID, static_cast<LPVOID>(rgbHash), &cb)) | ||
644 | { | ||
645 | ExitWithLastError(hr, "Failed to get certificate SHA1 hash property."); | ||
646 | } | ||
647 | AssertSz(cb == cbHash, "Did not correctly read certificate SHA1 hash."); | ||
648 | |||
649 | LExit: | ||
650 | if (pCertContext) | ||
651 | { | ||
652 | ::CertFreeCertificateContext(pCertContext); | ||
653 | } | ||
654 | |||
655 | if (hPfxCertStore) | ||
656 | { | ||
657 | ::CertCloseStore(hPfxCertStore, 0); | ||
658 | } | ||
659 | |||
660 | if (pPfxInfo) | ||
661 | { | ||
662 | HCRYPTPROV hProvIgnored = NULL; // ignored on deletes. | ||
663 | ::CryptAcquireContextW(&hProvIgnored, pPfxInfo->pwszContainerName, pPfxInfo->pwszProvName, pPfxInfo->dwProvType, dwKeyset | CRYPT_DELETEKEYSET | CRYPT_SILENT); | ||
664 | |||
665 | MemFree(pPfxInfo); | ||
666 | } | ||
667 | |||
668 | return hr; | ||
669 | } | ||
670 | |||
671 | |||
672 | static HRESULT FindExistingCertificate( | ||
673 | __in LPCWSTR wzName, | ||
674 | __in DWORD dwStoreLocation, | ||
675 | __in LPCWSTR wzStore, | ||
676 | __out BYTE** prgbCertificate, | ||
677 | __out DWORD* pcbCertificate | ||
678 | ) | ||
679 | { | ||
680 | HRESULT hr = S_OK; | ||
681 | HCERTSTORE hCertStore = NULL; | ||
682 | PCCERT_CONTEXT pCertContext = NULL; | ||
683 | BYTE* pbCertificate = NULL; | ||
684 | DWORD cbCertificate = 0; | ||
685 | |||
686 | hCertStore = ::CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, dwStoreLocation | CERT_STORE_READONLY_FLAG, wzStore); | ||
687 | MessageExitOnNullWithLastError(hCertStore, hr, msierrCERTFailedOpen, "Failed to open certificate store."); | ||
688 | |||
689 | // Loop through the certificate, looking for certificates that match our friendly name. | ||
690 | pCertContext = CertFindCertificateInStore(hCertStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL); | ||
691 | while (pCertContext) | ||
692 | { | ||
693 | WCHAR wzFriendlyName[256] = { 0 }; | ||
694 | DWORD cbFriendlyName = sizeof(wzFriendlyName); | ||
695 | |||
696 | if (::CertGetCertificateContextProperty(pCertContext, CERT_FRIENDLY_NAME_PROP_ID, reinterpret_cast<BYTE*>(wzFriendlyName), &cbFriendlyName) && | ||
697 | CSTR_EQUAL == ::CompareStringW(LOCALE_SYSTEM_DEFAULT, 0, wzName, -1, wzFriendlyName, -1)) | ||
698 | { | ||
699 | // If the certificate with matching friendly name is valid, let's use that. | ||
700 | long lVerify = ::CertVerifyTimeValidity(NULL, pCertContext->pCertInfo); | ||
701 | if (0 == lVerify) | ||
702 | { | ||
703 | cbCertificate = pCertContext->cbCertEncoded; | ||
704 | pbCertificate = static_cast<BYTE*>(MemAlloc(cbCertificate, FALSE)); | ||
705 | ExitOnNull(pbCertificate, hr, E_OUTOFMEMORY, "Failed to allocate memory to copy out exist certificate."); | ||
706 | |||
707 | CopyMemory(pbCertificate, pCertContext->pbCertEncoded, cbCertificate); | ||
708 | break; // found a matching certificate, no more searching necessary | ||
709 | } | ||
710 | } | ||
711 | |||
712 | // Next certificate in the store. | ||
713 | PCCERT_CONTEXT pNext = ::CertFindCertificateInStore(hCertStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, pCertContext); | ||
714 | // old pCertContext is freed by CertFindCertificateInStore | ||
715 | pCertContext = pNext; | ||
716 | } | ||
717 | |||
718 | *prgbCertificate = pbCertificate; | ||
719 | *pcbCertificate = cbCertificate; | ||
720 | pbCertificate = NULL; | ||
721 | |||
722 | LExit: | ||
723 | ReleaseMem(pbCertificate); | ||
724 | |||
725 | if (pCertContext) | ||
726 | { | ||
727 | ::CertFreeCertificateContext(pCertContext); | ||
728 | } | ||
729 | |||
730 | if (hCertStore) | ||
731 | { | ||
732 | ::CertCloseStore(hCertStore, 0); | ||
733 | } | ||
734 | |||
735 | return hr; | ||
736 | } | ||
737 | |||
738 | /* | ||
739 | HRESULT CreateEnroll(ICEnroll2 **hEnroll, INT iStore, INT iStoreLocation) | ||
740 | { | ||
741 | ICEnroll2 *pEnroll = NULL; | ||
742 | HRESULT hr = S_OK; | ||
743 | LONG lFlags; | ||
744 | DWORD dwFlags = iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT; | ||
745 | |||
746 | // create IEntroll | ||
747 | hr = CoCreateInstance( CLSID_CEnroll, NULL, CLSCTX_INPROC_SERVER, IID_ICEnroll2, (void **)&pEnroll ); | ||
748 | if (FAILED(hr)) | ||
749 | return hr; | ||
750 | |||
751 | switch (iStore) | ||
752 | { | ||
753 | case SCA_CERT_STORENAME_MY: | ||
754 | pEnroll->get_MyStoreFlags(&lFlags); | ||
755 | lFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK; | ||
756 | lFlags |= dwFlags; | ||
757 | // following call will change Request store flags also | ||
758 | pEnroll->put_MyStoreFlags(lFlags); | ||
759 | break; | ||
760 | case SCA_CERT_STORENAME_CA: | ||
761 | pEnroll->get_CAStoreFlags(&lFlags); | ||
762 | lFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK; | ||
763 | lFlags |= dwFlags; | ||
764 | // following call will change Request store flags also | ||
765 | pEnroll->put_CAStoreFlags(lFlags); | ||
766 | break; | ||
767 | case SCA_CERT_STORENAME_REQUEST: | ||
768 | pEnroll->get_RequestStoreFlags(&lFlags); | ||
769 | lFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK; | ||
770 | lFlags |= dwFlags; | ||
771 | // following call will change Request store flags also | ||
772 | pEnroll->put_RequestStoreFlags(lFlags); | ||
773 | break; | ||
774 | case SCA_CERT_STORENAME_ROOT: | ||
775 | pEnroll->get_RootStoreFlags(&lFlags); | ||
776 | lFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK; | ||
777 | lFlags |= dwFlags; | ||
778 | // following call will change Request store flags also | ||
779 | pEnroll->put_RootStoreFlags(lFlags); | ||
780 | break; | ||
781 | default: | ||
782 | hr = E_FAIL; | ||
783 | return hr; | ||
784 | } | ||
785 | |||
786 | pEnroll->get_GenKeyFlags(&lFlags); | ||
787 | lFlags |= CRYPT_EXPORTABLE; | ||
788 | pEnroll->put_GenKeyFlags(lFlags); | ||
789 | |||
790 | pEnroll->put_KeySpec(AT_KEYEXCHANGE); | ||
791 | pEnroll->put_ProviderType(PROV_RSA_SCHANNEL); | ||
792 | pEnroll->put_DeleteRequestCert(TRUE); | ||
793 | |||
794 | *hEnroll = pEnroll; | ||
795 | return hr; | ||
796 | } | ||
797 | |||
798 | |||
799 | HRESULT RequestCertificate(LPCWSTR pwzName, INT iStore, INT iStoreLocation, | ||
800 | LPCWSTR wzComputerName, LPCWSTR wzDistinguishedName, LPCWSTR wzCertificateAuthority, | ||
801 | BSTR *pbstrCertificate) | ||
802 | { | ||
803 | if (pbstrCertificate == NULL) | ||
804 | return E_INVALIDARG; | ||
805 | |||
806 | HRESULT hr; | ||
807 | ICEnroll2 *pEnroll = NULL; | ||
808 | ICertRequest *pCertRequest = NULL; | ||
809 | BSTR bstrRequest = NULL; | ||
810 | LONG nDisposition; | ||
811 | |||
812 | BSTR bstrCertificateUsage = NULL; | ||
813 | BSTR bstrCertificateAttributes = NULL; | ||
814 | BSTR bstrCertificateAuthority = NULL; | ||
815 | |||
816 | // equivalent to: sprintf(bstrDistinguishedName, L"%s,CN=%s", wzDistinguishedName, wzComputerName); | ||
817 | DWORD cchComputerName = lstrlenW(wzComputerName); | ||
818 | DWORD cchDistinguishedName = lstrlenW(wzDistinguishedName); | ||
819 | CONST DWORD cchbstrDistinguishedName = 5 + cchComputerName + cchDistinguishedName; | ||
820 | BSTR bstrDistinguishedName = SysAllocStringLen(NULL, cchbstrDistinguishedName); | ||
821 | ExitOnNull(bstrDistinguishedName, hr, E_OUTOFMEMORY, "Failed to allocate space for distinguished name."); | ||
822 | ::StringCchCopyW((WCHAR*) bstrDistinguishedName, cchbstrDistinguishedName, wzDistinguishedName); | ||
823 | ::StringCchCatW((WCHAR*) bstrDistinguishedName, cchbstrDistinguishedName, L",CN="); | ||
824 | ::StringCchCatW((WCHAR*) bstrDistinguishedName, cchbstrDistinguishedName, wzComputerName); | ||
825 | |||
826 | bstrCertificateUsage = SysAllocString(WIDE(szOID_PKIX_KP_SERVER_AUTH)); | ||
827 | ExitOnNull(bstrCertificateUsage, hr, E_OUTOFMEMORY, "Failed to allocate space for Certificate Usage."); | ||
828 | bstrCertificateAttributes = SysAllocString(L"CertificateTemplate:WebServer"); | ||
829 | bstrCertificateAuthority = SysAllocString(wzCertificateAuthority); | ||
830 | ExitOnNull(bstrCertificateAuthority, hr, E_OUTOFMEMORY, "Failed to allocate space for Certificate Authority."); | ||
831 | |||
832 | hr = CreateEnroll(&pEnroll, iStore, iStoreLocation); | ||
833 | ExitOnFailure(hr, "failed CoCreateInstance IEnroll"); | ||
834 | |||
835 | hr = pEnroll->createPKCS10(bstrDistinguishedName, bstrCertificateUsage, &bstrRequest); | ||
836 | ExitOnFailure(hr, "failed createPKCS10"); | ||
837 | |||
838 | hr = CoCreateInstance(CLSID_CCertRequest, NULL, CLSCTX_INPROC_SERVER, IID_ICertRequest, (void **)&pCertRequest); | ||
839 | ExitOnFailure(hr, "failed CoCreateInstance ICertRequest"); | ||
840 | |||
841 | hr = pCertRequest->Submit(CR_IN_BASE64 | CR_IN_PKCS10, bstrRequest, bstrCertificateAttributes, bstrCertificateAuthority, &nDisposition); | ||
842 | ExitOnFailure(hr, "failed ICertRequest.Submit"); | ||
843 | |||
844 | hr = (nDisposition == CR_DISP_ISSUED) ? S_OK : E_FAIL; | ||
845 | ExitOnFailure(hr, "failed CR_DISP_ISSUED"); | ||
846 | |||
847 | hr = pCertRequest->GetCertificate(CR_OUT_BASE64, pbstrCertificate); | ||
848 | ExitOnFailure(hr, "failed ICertRequest.GetCertificate"); | ||
849 | |||
850 | // save the certificate in place, cannot be passed to a deferred custom action | ||
851 | hr = pEnroll->acceptPKCS7(*pbstrCertificate); | ||
852 | ExitOnFailure(hr, "failed accept certificate into MY store"); | ||
853 | |||
854 | LExit: | ||
855 | ReleaseObject(pCertRequest); | ||
856 | ReleaseBSTR(bstrRequest); | ||
857 | ReleaseObject(pEnroll); | ||
858 | ReleaseBSTR(bstrCertificateAuthority); | ||
859 | ReleaseBSTR(bstrCertificateAttributes); | ||
860 | ReleaseBSTR(bstrDistinguishedName); | ||
861 | |||
862 | return hr; | ||
863 | } | ||
864 | |||
865 | |||
866 | VOID ParseCertificateAuthority(__in LPCWSTR wzCertificateAuthorityOrig, __out LPWSTR *pwzBuffer, __out LPWSTR **hwzCAArray, __out int *piCAArray) | ||
867 | { | ||
868 | // @asAuthorities = split /;/, $sAuthority; | ||
869 | CONST WCHAR wchDelimiter = L';'; | ||
870 | |||
871 | // copy constant into a buffer | ||
872 | Assert(wzCertificateAuthorityOrig); | ||
873 | |||
874 | INT cchCA = lstrlenW(wzCertificateAuthorityOrig) + 1; | ||
875 | WCHAR* wzBuffer = new WCHAR[cchCA]; | ||
876 | if (!wzBuffer) | ||
877 | return; | ||
878 | |||
879 | ::StringCchCopyW(wzBuffer, cchCA, wzCertificateAuthorityOrig); | ||
880 | |||
881 | // determine the number of strings in the field | ||
882 | int iCAArray = 1; | ||
883 | int i; | ||
884 | for (i = 0; i < cchCA; ++i) | ||
885 | { | ||
886 | if (wzBuffer[i] == wchDelimiter) | ||
887 | ++iCAArray; | ||
888 | } | ||
889 | LPWSTR *pwzCAArray = (LPWSTR*) new BYTE[iCAArray * sizeof(LPWSTR)]; | ||
890 | if (!pwzCAArray) | ||
891 | { | ||
892 | return; | ||
893 | } | ||
894 | |||
895 | pwzCAArray[0] = wzBuffer; | ||
896 | iCAArray = 0; | ||
897 | for (i = 0; i < cchCA; ++i) | ||
898 | { | ||
899 | if (wzBuffer[i] != wchDelimiter) | ||
900 | continue; | ||
901 | wzBuffer[i] = 0; // convert buffer into MULTISZ | ||
902 | pwzCAArray[iCAArray] = &wzBuffer[i+1]; | ||
903 | ++iCAArray; | ||
904 | } | ||
905 | |||
906 | *pwzBuffer = wzBuffer; | ||
907 | *hwzCAArray = pwzCAArray; | ||
908 | *piCAArray = iCAArray; | ||
909 | } | ||
910 | |||
911 | |||
912 | HRESULT ScaSslExistingCertificateByBinaryData(INT iStore, INT iStoreLocation, BYTE* pwzData, DWORD cchData) | ||
913 | { | ||
914 | HRESULT hr = S_FALSE; | ||
915 | HCERTSTORE hCertStore = NULL; | ||
916 | PCCERT_CONTEXT pCertCtx = NULL, pCertCtxExisting = NULL; | ||
917 | DWORD dwFlags = 0; | ||
918 | LPCWSTR wzStore = StoreMapping(iStore); | ||
919 | CERT_BLOB blob; | ||
920 | |||
921 | dwFlags = iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT; | ||
922 | hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, dwFlags, wzStore); | ||
923 | MessageExitOnNullWithLastError(hCertStore, hr, msierrCERTFailedOpen, "failed to open certificate store, OK on uninstall"); | ||
924 | |||
925 | blob.pbData = pwzData; | ||
926 | blob.cbData = cchData; | ||
927 | |||
928 | if (!::CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, | ||
929 | 0, NULL, NULL, NULL, NULL, NULL, (LPCVOID*)&pCertCtx)) | ||
930 | ExitOnLastError(hr, "failed to parse the certificate blob, OK on uninstall"); | ||
931 | |||
932 | pCertCtxExisting = CertFindCertificateInStore( | ||
933 | hCertStore, | ||
934 | PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, | ||
935 | 0, | ||
936 | CERT_FIND_EXISTING, | ||
937 | pCertCtx, | ||
938 | NULL); | ||
939 | |||
940 | if (pCertCtxExisting) | ||
941 | { | ||
942 | hr = S_OK; | ||
943 | } | ||
944 | |||
945 | LExit: | ||
946 | if (pCertCtx) | ||
947 | { | ||
948 | CertFreeCertificateContext(pCertCtx); | ||
949 | pCertCtx = NULL; | ||
950 | } | ||
951 | if (pCertCtxExisting) | ||
952 | { | ||
953 | CertFreeCertificateContext(pCertCtxExisting); | ||
954 | pCertCtxExisting = NULL; | ||
955 | } | ||
956 | if (hCertStore) | ||
957 | { | ||
958 | CertCloseStore(hCertStore, 0); | ||
959 | hCertStore = NULL; | ||
960 | } | ||
961 | |||
962 | return hr; | ||
963 | } | ||
964 | |||
965 | |||
966 | HRESULT ScaSslExistingCertificateByName(LPCWSTR pwzName, INT iStore, INT iStoreLocation, | ||
967 | BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer) | ||
968 | { | ||
969 | HRESULT hr = S_FALSE; | ||
970 | HCERTSTORE hSystemStore = NULL; | ||
971 | PCCERT_CONTEXT pTargetCert = NULL; | ||
972 | WCHAR wzFriendlyName[MAX_PATH] = {0}; | ||
973 | DWORD dwFriendlyNameLen = sizeof(wzFriendlyName); | ||
974 | |||
975 | // Call CertOpenStore to open the CA store. | ||
976 | hSystemStore = CertOpenStore( | ||
977 | CERT_STORE_PROV_SYSTEM_REGISTRY, | ||
978 | 0, | ||
979 | NULL, | ||
980 | (iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT) | CERT_STORE_OPEN_EXISTING_FLAG, | ||
981 | StoreMapping(iStore)); | ||
982 | if (hSystemStore == NULL) | ||
983 | ExitFunction(); | ||
984 | |||
985 | // Get a particular certificate using CertFindCertificateInStore. | ||
986 | pTargetCert = CertFindCertificateInStore( | ||
987 | hSystemStore, | ||
988 | PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, | ||
989 | 0, | ||
990 | CERT_FIND_ANY, | ||
991 | NULL, | ||
992 | NULL); | ||
993 | while (pTargetCert != NULL) | ||
994 | { | ||
995 | if ((CertGetCertificateContextProperty(pTargetCert, CERT_FRIENDLY_NAME_PROP_ID, | ||
996 | (BYTE*)wzFriendlyName, &dwFriendlyNameLen)) && | ||
997 | lstrcmpW(wzFriendlyName, pwzName) == 0) | ||
998 | { | ||
999 | // pTargetCert is a pointer to the desired certificate. | ||
1000 | // Check the certificate's validity. | ||
1001 | switch (CertVerifyTimeValidity( | ||
1002 | NULL, | ||
1003 | pTargetCert->pCertInfo)) | ||
1004 | { | ||
1005 | case 1: | ||
1006 | // Certificate is expired | ||
1007 | WcaLog(LOGMSG_STANDARD, "The SSL certificate has expired"); | ||
1008 | // always remove it | ||
1009 | { | ||
1010 | PCCERT_CONTEXT pDupCertContext = CertDuplicateCertificateContext(pTargetCert); | ||
1011 | if (pDupCertContext && CertDeleteCertificateFromStore(pDupCertContext)) | ||
1012 | { | ||
1013 | WcaLog(LOGMSG_STANDARD, "A SSL certificate has removed"); | ||
1014 | } | ||
1015 | } | ||
1016 | break; | ||
1017 | case 0: | ||
1018 | // Certificate is valid | ||
1019 | WcaLog(LOGMSG_STANDARD, "The SSL certificate is valid"); | ||
1020 | hr = S_OK; | ||
1021 | if (pbaHashBuffer) | ||
1022 | { | ||
1023 | // if the certificate already exists and is valid, use that one | ||
1024 | DWORD dwHashSize = CB_CERTIFICATE_HASH; | ||
1025 | hr = CertGetCertificateContextProperty(pTargetCert, CERT_SHA1_HASH_PROP_ID, (VOID*)pbaHashBuffer, &dwHashSize) | ||
1026 | ? S_OK : E_FAIL; | ||
1027 | ExitOnFailure(hr, "failed CertGetCertificateContextProperty CERT_SHA1_HASH_PROP_ID"); | ||
1028 | Assert(pbstrCertificate); | ||
1029 | Assert(pcbCertificate); | ||
1030 | ReleaseBSTR(*pbstrCertificate); | ||
1031 | |||
1032 | *pbstrCertificate = SysAllocStringByteLen((LPCSTR)(pTargetCert->pbCertEncoded), pTargetCert->cbCertEncoded); | ||
1033 | *pcbCertificate = pTargetCert->cbCertEncoded; | ||
1034 | } | ||
1035 | ExitFunction(); | ||
1036 | break; | ||
1037 | default: | ||
1038 | // Certificate not valid yet, ignore it | ||
1039 | WcaLog(LOGMSG_STANDARD, "The SSL certificate is not valid"); | ||
1040 | break; | ||
1041 | } | ||
1042 | } | ||
1043 | pTargetCert = CertFindCertificateInStore( | ||
1044 | hSystemStore, | ||
1045 | PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, | ||
1046 | 0, | ||
1047 | CERT_FIND_ANY, | ||
1048 | NULL, | ||
1049 | pTargetCert); | ||
1050 | wzFriendlyName[0] = 0; | ||
1051 | dwFriendlyNameLen = sizeof(wzFriendlyName); | ||
1052 | } | ||
1053 | |||
1054 | LExit: | ||
1055 | // Clean up memory and quit. | ||
1056 | if (pTargetCert) | ||
1057 | { | ||
1058 | CertFreeCertificateContext(pTargetCert); | ||
1059 | pTargetCert = NULL; | ||
1060 | } | ||
1061 | if (hSystemStore) | ||
1062 | { | ||
1063 | CertCloseStore(hSystemStore, CERT_CLOSE_STORE_CHECK_FLAG); | ||
1064 | hSystemStore = NULL; | ||
1065 | } | ||
1066 | |||
1067 | return hr; | ||
1068 | } | ||
1069 | |||
1070 | |||
1071 | HRESULT ScaSslNewCertificate(LPCWSTR pwzName, INT iStore, INT iStoreLocation, LPCWSTR wzComputerName, LPCWSTR wzDistinguishedName, LPCWSTR wzCertificateAuthorityOrig, | ||
1072 | BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer) | ||
1073 | { | ||
1074 | |||
1075 | if (pbstrCertificate == NULL) | ||
1076 | return E_INVALIDARG; | ||
1077 | |||
1078 | HRESULT hr = S_OK; | ||
1079 | LPWSTR wzCABuffer = NULL; | ||
1080 | LPWSTR *wzCAArray = NULL; | ||
1081 | int iCAArray = 0; | ||
1082 | |||
1083 | // otherwise call the CA for one | ||
1084 | ParseCertificateAuthority(wzCertificateAuthorityOrig, &wzCABuffer, &wzCAArray, &iCAArray); | ||
1085 | |||
1086 | // try each authority three times | ||
1087 | for (int i = 0; i < 3 * iCAArray; ++i) | ||
1088 | { | ||
1089 | LPCWSTR wzCA = wzCAArray[i % iCAArray]; | ||
1090 | if (NULL == wzCA || NULL == wzCA[0]) continue; | ||
1091 | WcaLog(LOGMSG_STANDARD, "Requesting SSL certificate from %ls", wzCA); | ||
1092 | hr = RequestCertificate(pwzName, iStore, iStoreLocation, wzComputerName, wzDistinguishedName, wzCA, pbstrCertificate); | ||
1093 | if (hr == S_OK && pbstrCertificate) | ||
1094 | { | ||
1095 | // set the friendly name | ||
1096 | CRYPT_HASH_BLOB hblob; | ||
1097 | CERT_BLOB blob; | ||
1098 | HCERTSTORE hCertStore = NULL; | ||
1099 | PCCERT_CONTEXT pCertCtxExisting = NULL; | ||
1100 | |||
1101 | blob.pbData = (BYTE*)pwzName; | ||
1102 | blob.cbData = (lstrlenW(pwzName) + 1) * sizeof(pwzName[0]); // including terminating null | ||
1103 | |||
1104 | *pcbCertificate = SysStringByteLen(*pbstrCertificate); | ||
1105 | hr = CertificateToHash(*pbstrCertificate, pbaHashBuffer); | ||
1106 | ExitOnFailure(hr, "failed to CertificateToHash for an existing certificate"); | ||
1107 | |||
1108 | hblob.pbData = pbaHashBuffer; | ||
1109 | hblob.cbData = CB_CERTIFICATE_HASH; | ||
1110 | |||
1111 | hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, (iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT), StoreMapping(iStore)); | ||
1112 | MessageExitOnNullWithLastError(hCertStore, hr, msierrCERTFailedOpen, "failed to open certificate store"); | ||
1113 | |||
1114 | pCertCtxExisting = CertFindCertificateInStore( | ||
1115 | hCertStore, | ||
1116 | PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, | ||
1117 | 0, | ||
1118 | CERT_FIND_HASH, | ||
1119 | &hblob, | ||
1120 | NULL); | ||
1121 | |||
1122 | if (pCertCtxExisting) | ||
1123 | { | ||
1124 | CertSetCertificateContextProperty( | ||
1125 | pCertCtxExisting, | ||
1126 | CERT_FRIENDLY_NAME_PROP_ID, | ||
1127 | 0, | ||
1128 | &blob); | ||
1129 | } | ||
1130 | |||
1131 | if (pCertCtxExisting) | ||
1132 | { | ||
1133 | CertFreeCertificateContext(pCertCtxExisting); | ||
1134 | pCertCtxExisting = NULL; | ||
1135 | } | ||
1136 | if (hCertStore) | ||
1137 | { | ||
1138 | CertCloseStore(hCertStore, 0); | ||
1139 | hCertStore = NULL; | ||
1140 | } | ||
1141 | ExitFunction(); | ||
1142 | } | ||
1143 | if (pbstrCertificate && *pbstrCertificate) | ||
1144 | { | ||
1145 | SysFreeString(*pbstrCertificate); | ||
1146 | pbstrCertificate = NULL; | ||
1147 | } | ||
1148 | } | ||
1149 | hr = E_FAIL; | ||
1150 | ExitOnFailure(hr, "failed to RequestCertificate"); | ||
1151 | |||
1152 | LExit: | ||
1153 | if (wzCABuffer) | ||
1154 | { | ||
1155 | delete wzCABuffer; | ||
1156 | } | ||
1157 | if (wzCAArray) | ||
1158 | { | ||
1159 | delete wzCAArray; | ||
1160 | } | ||
1161 | |||
1162 | return hr; | ||
1163 | } | ||
1164 | |||
1165 | |||
1166 | HRESULT ScaGetCertificateByRequest(LPCWSTR pwzName, BOOL fIsInstalling, BOOL fIsUninstalling, | ||
1167 | INT iStore, INT iStoreLocation, | ||
1168 | LPCWSTR wzDistinguishedName, LPCWSTR wzCA, | ||
1169 | BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer) | ||
1170 | { | ||
1171 | HRESULT hr = S_OK; | ||
1172 | WCHAR wzComputerName[MAX_COMPUTER_NAME] = {0}; | ||
1173 | WCHAR* pwzData = NULL; | ||
1174 | DWORD cchData = 0; | ||
1175 | |||
1176 | // override %COMPUTERNAME% with DOMAINNAME property | ||
1177 | hr = WcaGetProperty( L"DOMAINNAME", &pwzData); | ||
1178 | ExitOnFailure(hr, "Failed to get Property DOMAINNAME"); | ||
1179 | if (*pwzData) | ||
1180 | { | ||
1181 | // if DOMAINNAME is set, use it | ||
1182 | ::StringCchCopyW(wzComputerName, MAX_COMPUTER_NAME, pwzData); | ||
1183 | } | ||
1184 | else | ||
1185 | { | ||
1186 | // otherwise get the intranet name given by %COMPUTERNAME% | ||
1187 | GetEnvironmentVariableW(L"COMPUTERNAME", wzComputerName, MAX_COMPUTER_NAME); | ||
1188 | } | ||
1189 | |||
1190 | hr = ScaSslExistingCertificateByName(pwzName, iStore, iStoreLocation, pbstrCertificate, pcbCertificate, pbaHashBuffer); | ||
1191 | ExitOnFailure(hr, "Failed ScaSslExistingCertificateByName"); | ||
1192 | if (S_OK != hr) | ||
1193 | { | ||
1194 | if (!fIsUninstalling && fIsInstalling) | ||
1195 | { | ||
1196 | // if no existing cert and not on uninstall, hit the authority | ||
1197 | WcaLog(LOGMSG_STANDARD, "Adding certificate: requested, %ls", wzDistinguishedName); | ||
1198 | hr = ScaSslNewCertificate(pwzName, iStore, iStoreLocation, wzComputerName, wzDistinguishedName, wzCA, | ||
1199 | pbstrCertificate, pcbCertificate, pbaHashBuffer); | ||
1200 | ExitOnFailure(hr, "Failed ScaSslNewCertificate"); | ||
1201 | } | ||
1202 | else | ||
1203 | { | ||
1204 | // if no existing cert and uninstall | ||
1205 | hr = S_OK; | ||
1206 | } | ||
1207 | } | ||
1208 | |||
1209 | LExit: | ||
1210 | ReleaseStr(pwzData); | ||
1211 | |||
1212 | return hr; | ||
1213 | } | ||
1214 | |||
1215 | |||
1216 | HRESULT ScaInstallCertificateByContext(LPCWSTR pwzName, INT iStore, INT iStoreLocation, | ||
1217 | PCCERT_CONTEXT pCertContext) | ||
1218 | { | ||
1219 | HRESULT hr = S_OK; | ||
1220 | HCERTSTORE hCertStore = NULL; | ||
1221 | DWORD dwFlags = iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT; | ||
1222 | CERT_BLOB blob; | ||
1223 | |||
1224 | hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, dwFlags, StoreMapping(iStore)); | ||
1225 | if (hCertStore == NULL) | ||
1226 | MessageExitOnLastError(hr, msierrCERTFailedOpen, "failed to open certificate store"); | ||
1227 | |||
1228 | blob.pbData = (BYTE*)pwzName; | ||
1229 | blob.cbData = (lstrlenW(pwzName) + 1) * sizeof(pwzName[0]); // including terminating null | ||
1230 | CertSetCertificateContextProperty( | ||
1231 | pCertContext, | ||
1232 | CERT_FRIENDLY_NAME_PROP_ID, | ||
1233 | 0, | ||
1234 | &blob); | ||
1235 | |||
1236 | if (!CertAddCertificateContextToStore( | ||
1237 | hCertStore, | ||
1238 | pCertContext, | ||
1239 | CERT_STORE_ADD_REPLACE_EXISTING, | ||
1240 | NULL)) | ||
1241 | { | ||
1242 | hr = E_FAIL; | ||
1243 | MessageExitOnLastError(hr, msierrCERTFailedAdd, "failed to add certificate to the store"); | ||
1244 | } | ||
1245 | |||
1246 | LExit: | ||
1247 | if (hCertStore) | ||
1248 | { | ||
1249 | CertCloseStore(hCertStore, 0); | ||
1250 | hCertStore = NULL; | ||
1251 | } | ||
1252 | |||
1253 | return hr; | ||
1254 | } | ||
1255 | |||
1256 | |||
1257 | HRESULT ScaGetCertificateByPath(LPCWSTR pwzName, BOOL fIsInstalling, BOOL fIsUninstalling, | ||
1258 | INT iStore, INT iStoreLocation, LPCWSTR wzSslCertificate, LPCWSTR wzPFXPassword, | ||
1259 | BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer) | ||
1260 | { | ||
1261 | Assert(wzSslCertificate); | ||
1262 | HRESULT hr = S_OK; | ||
1263 | PCCERT_CONTEXT pCertContext = NULL; | ||
1264 | DWORD dwEncodingType = 0; | ||
1265 | DWORD dwContentType = 0; | ||
1266 | DWORD dwFormatType = 0; | ||
1267 | DWORD dwHashSize = CB_CERTIFICATE_HASH; | ||
1268 | HANDLE hPfxFile = INVALID_HANDLE_VALUE; | ||
1269 | CRYPT_DATA_BLOB blob; | ||
1270 | |||
1271 | blob.pbData = NULL; | ||
1272 | blob.cbData = 0; | ||
1273 | |||
1274 | if (wzSslCertificate && wzSslCertificate[0] != 0) | ||
1275 | { | ||
1276 | if (!::CryptQueryObject(CERT_QUERY_OBJECT_FILE, (LPVOID)wzSslCertificate, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, | ||
1277 | 0, &dwEncodingType, &dwContentType, &dwFormatType, NULL, NULL, (LPCVOID*)&pCertContext)) | ||
1278 | hr = fIsUninstalling ? S_FALSE : HRESULT_FROM_WIN32(::GetLastError()); // don't fail on uninstall | ||
1279 | ExitOnFailure(hr, "failed CryptQueryObject"); | ||
1280 | } | ||
1281 | else | ||
1282 | { | ||
1283 | hr = S_FALSE; | ||
1284 | ExitFunction(); | ||
1285 | } | ||
1286 | |||
1287 | if (!pCertContext) | ||
1288 | { | ||
1289 | // this is a pfx? | ||
1290 | // make sure to exit this block of code properly for clean up blob.pbData | ||
1291 | if (dwContentType & CERT_QUERY_CONTENT_PFX) | ||
1292 | { | ||
1293 | DWORD iSize = 0, iReadSize = 0; | ||
1294 | HCERTSTORE hPfxCertStore = NULL; | ||
1295 | |||
1296 | hPfxFile = ::CreateFileW(wzSslCertificate, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | ||
1297 | NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | ||
1298 | hr = (hPfxFile != INVALID_HANDLE_VALUE) ? S_OK : E_FAIL; | ||
1299 | ExitOnFailure(hr, "failed CryptQueryObject, file handle is null"); | ||
1300 | iSize = ::GetFileSize(hPfxFile, NULL); | ||
1301 | hr = (iSize > 0) ? S_OK : E_FAIL; | ||
1302 | ExitOnFailure(hr, "failed CryptQueryObject, file size is 0"); | ||
1303 | blob.pbData = new BYTE[iSize]; | ||
1304 | blob.cbData = iSize; | ||
1305 | hr = (blob.pbData) ? S_OK : E_FAIL; | ||
1306 | ExitOnFailure(hr, "out of memory for blob"); | ||
1307 | |||
1308 | if (::ReadFile(hPfxFile, (LPVOID)blob.pbData, iSize, &iReadSize, NULL)) | ||
1309 | { | ||
1310 | hPfxCertStore = PFXImportCertStore((CRYPT_DATA_BLOB*)&blob, wzPFXPassword, | ||
1311 | (iStoreLocation == SCA_CERTSYSTEMSTORE_CURRENTUSER) ? CRYPT_USER_KEYSET : CRYPT_MACHINE_KEYSET); | ||
1312 | if (hPfxCertStore) | ||
1313 | { | ||
1314 | pCertContext = CertEnumCertificatesInStore(hPfxCertStore, NULL); | ||
1315 | // work only with the first certificate in pfx | ||
1316 | if (pCertContext) | ||
1317 | { | ||
1318 | hr = CertGetCertificateContextProperty(pCertContext, CERT_SHA1_HASH_PROP_ID, (VOID*)pbaHashBuffer, &dwHashSize) | ||
1319 | ? S_OK : E_FAIL; | ||
1320 | ExitOnFailure(hr, "failed CertGetCertificateContextProperty CERT_SHA1_HASH_PROP_ID"); | ||
1321 | ReleaseBSTR(*pbstrCertificate); | ||
1322 | |||
1323 | *pbstrCertificate = SysAllocStringByteLen((LPCSTR)(pCertContext->pbCertEncoded), pCertContext->cbCertEncoded); | ||
1324 | *pcbCertificate = pCertContext->cbCertEncoded; | ||
1325 | if (fIsInstalling) | ||
1326 | { | ||
1327 | // install the certificate, cannot defer because the data required cannot be passed | ||
1328 | hr = ScaInstallCertificateByContext(pwzName, iStore, iStoreLocation, pCertContext); | ||
1329 | } | ||
1330 | } | ||
1331 | else | ||
1332 | hr = E_FAIL; | ||
1333 | } | ||
1334 | else | ||
1335 | hr = E_FAIL; | ||
1336 | } | ||
1337 | else | ||
1338 | hr = E_FAIL; | ||
1339 | } | ||
1340 | else | ||
1341 | { | ||
1342 | ExitOnFailure(hr = E_FAIL, "failed CryptQueryObject, unknown data"); | ||
1343 | } | ||
1344 | } | ||
1345 | else | ||
1346 | { | ||
1347 | // return cert and its hash | ||
1348 | hr = CertGetCertificateContextProperty(pCertContext, CERT_SHA1_HASH_PROP_ID, (VOID*)pbaHashBuffer, &dwHashSize) | ||
1349 | ? S_OK : E_FAIL; | ||
1350 | ExitOnFailure(hr, "failed CertGetCertificateContextProperty CERT_SHA1_HASH_PROP_ID"); | ||
1351 | ReleaseBSTR(*pbstrCertificate); | ||
1352 | |||
1353 | *pbstrCertificate = SysAllocStringByteLen((LPCSTR)(pCertContext->pbCertEncoded), pCertContext->cbCertEncoded); | ||
1354 | *pcbCertificate = pCertContext->cbCertEncoded; | ||
1355 | if (fIsInstalling) | ||
1356 | { | ||
1357 | // install the certificate, cannot defer because the data required cannot be passed | ||
1358 | hr = ScaInstallCertificateByContext(pwzName, iStore, iStoreLocation, pCertContext); | ||
1359 | } | ||
1360 | } | ||
1361 | |||
1362 | LExit: | ||
1363 | if (pCertContext) | ||
1364 | { | ||
1365 | CertFreeCertificateContext(pCertContext); | ||
1366 | pCertContext = NULL; | ||
1367 | } | ||
1368 | if (hPfxFile != INVALID_HANDLE_VALUE) | ||
1369 | { | ||
1370 | CloseHandle(hPfxFile); | ||
1371 | hPfxFile = INVALID_HANDLE_VALUE; | ||
1372 | } | ||
1373 | if (blob.pbData) | ||
1374 | { | ||
1375 | delete [] blob.pbData; | ||
1376 | blob.pbData = NULL; | ||
1377 | blob.cbData = 0; | ||
1378 | } | ||
1379 | |||
1380 | return hr; | ||
1381 | } | ||
1382 | |||
1383 | |||
1384 | HRESULT ScaInstallCertificateByBinaryData(BOOL fAddCert, INT iStore, INT iStoreLocation, LPCWSTR wzName, BYTE* pwzData, DWORD cchData, | ||
1385 | LPCWSTR wzPFXPassword) | ||
1386 | { | ||
1387 | Assert(wzName); | ||
1388 | Assert(pwzData); | ||
1389 | Assert(cchData); | ||
1390 | HRESULT hr = S_OK; | ||
1391 | HCERTSTORE hCertStore = NULL, hPfxCertStore = NULL; | ||
1392 | PCCERT_CONTEXT pCertCtx = NULL, pCertCtxExisting = NULL; | ||
1393 | DWORD dwFlags, dwEncodingType, dwContentType, dwFormatType; | ||
1394 | CERT_BLOB blob; | ||
1395 | LPCWSTR wzStore = StoreMapping(iStore); | ||
1396 | |||
1397 | dwFlags = iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT; | ||
1398 | hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, dwFlags, wzStore); | ||
1399 | MessageExitOnNullWithLastError(hCertStore, hr, msierrCERTFailedOpen, "failed to open certificate store"); | ||
1400 | |||
1401 | blob.pbData = pwzData; | ||
1402 | blob.cbData = cchData; | ||
1403 | |||
1404 | if (!::CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, | ||
1405 | 0, &dwEncodingType, &dwContentType, &dwFormatType, NULL, NULL, (LPCVOID*)&pCertCtx)) | ||
1406 | ExitOnLastError(hr, "failed to parse the certificate blob"); | ||
1407 | ExitOnNull(pCertCtx, hr, E_UNEXPECTED, "failed to parse the certificate blob"); | ||
1408 | |||
1409 | blob.pbData = (BYTE*)wzName; | ||
1410 | blob.cbData = (lstrlenW(wzName) + 1) * sizeof(wzName[0]); // including terminating null | ||
1411 | |||
1412 | CertSetCertificateContextProperty( | ||
1413 | pCertCtx, | ||
1414 | CERT_FRIENDLY_NAME_PROP_ID, | ||
1415 | 0, | ||
1416 | &blob); | ||
1417 | |||
1418 | if (fAddCert) | ||
1419 | { | ||
1420 | // Add | ||
1421 | WcaLog(LOGMSG_STANDARD, "Adding certificate: binary name, %ls", wzName); | ||
1422 | if (!CertAddCertificateContextToStore( | ||
1423 | hCertStore, | ||
1424 | pCertCtx, | ||
1425 | CERT_STORE_ADD_REPLACE_EXISTING, | ||
1426 | NULL)) | ||
1427 | { | ||
1428 | hr = E_FAIL; | ||
1429 | MessageExitOnLastError(hr, msierrCERTFailedAdd, "failed to add certificate to the store"); | ||
1430 | } | ||
1431 | } | ||
1432 | else | ||
1433 | { | ||
1434 | // Delete | ||
1435 | WcaLog(LOGMSG_STANDARD, "Deleting certificate provided: binary name, %ls", wzName); | ||
1436 | pCertCtxExisting = CertFindCertificateInStore( | ||
1437 | hCertStore, | ||
1438 | PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, | ||
1439 | 0, | ||
1440 | CERT_FIND_EXISTING, | ||
1441 | pCertCtx, | ||
1442 | NULL); | ||
1443 | |||
1444 | if (pCertCtxExisting) | ||
1445 | { | ||
1446 | if (!CertDeleteCertificateFromStore(pCertCtxExisting)) | ||
1447 | { | ||
1448 | ExitOnLastError(hr, "failed to delete certificate"); | ||
1449 | } | ||
1450 | else | ||
1451 | { | ||
1452 | pCertCtxExisting = NULL; | ||
1453 | } | ||
1454 | } | ||
1455 | } | ||
1456 | |||
1457 | LExit: | ||
1458 | if (pCertCtx) | ||
1459 | { | ||
1460 | CertFreeCertificateContext(pCertCtx); | ||
1461 | pCertCtx = NULL; | ||
1462 | } | ||
1463 | if (pCertCtxExisting) | ||
1464 | { | ||
1465 | CertFreeCertificateContext(pCertCtxExisting); | ||
1466 | pCertCtxExisting = NULL; | ||
1467 | } | ||
1468 | // order is important for store | ||
1469 | if (hCertStore) | ||
1470 | { | ||
1471 | CertCloseStore(hCertStore, 0); | ||
1472 | hCertStore = NULL; | ||
1473 | } | ||
1474 | if (hPfxCertStore) | ||
1475 | { | ||
1476 | CertCloseStore(hPfxCertStore, 0); | ||
1477 | hPfxCertStore = NULL; | ||
1478 | } | ||
1479 | |||
1480 | return hr; | ||
1481 | } | ||
1482 | */ | ||