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