main
cpp 1,486 lines 52.7 KB
Raw
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 `Wix4Certificate`";
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"Wix4Certificate"))
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 Wix4CertificateHash table.
496 hr = WcaAddTempRecord(&hCertificateHashView, &hCertificateHashColumns, L"Wix4CertificateHash", NULL, 0, 2, wzId, wzEncodedCertificateHash);
497 ExitOnFailure(hr, "Failed to add encoded hash 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 SIZE_T size = 0;
556
557 hr = FileRead(&pbData, &size, wzPath);
558 ExitOnFailure(hr, "Failed to read PFX file: %ls", wzPath);
559
560 cbData = (DWORD)size;
561 }
562 else
563 {
564 hr = E_UNEXPECTED;
565 ExitOnFailure(hr, "Unexpected certificate type read from disk.");
566 }
567 }
568
569 *pcbData = cbData;
570 *prgbData = pbData;
571 pbData = NULL;
572
573 LExit:
574 ReleaseMem(pbData);
575 return hr;
576 }
577
578
579 static HRESULT CertificateToHash(
580 __in BYTE* pbCertificate,
581 __in DWORD cbCertificate,
582 __in DWORD dwStoreLocation,
583 __in LPCWSTR wzPFXPassword,
584 __in BYTE rgbHash[],
585 __in DWORD cbHash
586 )
587 {
588 HRESULT hr = S_OK;
589
590 HCERTSTORE hPfxCertStore = NULL;
591 PCCERT_CONTEXT pCertContext = NULL;
592 PCCERT_CONTEXT pCertContextEnum = NULL;
593 CRYPT_DATA_BLOB blob = { 0 };
594 CRYPT_KEY_PROV_INFO* pPfxInfo = NULL;
595 DWORD dwKeyset = (CERT_SYSTEM_STORE_CURRENT_USER == dwStoreLocation) ? CRYPT_USER_KEYSET : CRYPT_MACHINE_KEYSET;
596 DWORD dwEncodingType;
597 DWORD dwContentType;
598 DWORD dwFormatType;
599
600 blob.pbData = pbCertificate;
601 blob.cbData = cbCertificate;
602
603 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))
604 {
605 ExitWithLastError(hr, "Failed to process certificate as a valid certificate.");
606 }
607
608 if (!pCertContext)
609 {
610 // If we have a PFX blob, get the first certificate out of the PFX and use that instead of the PFX.
611 if (dwContentType & CERT_QUERY_CONTENT_PFX)
612 {
613 // If we fail and our password is blank, also try passing in NULL for the password (according to the docs)
614 hPfxCertStore = ::PFXImportCertStore((CRYPT_DATA_BLOB*)&blob, wzPFXPassword, dwKeyset);
615 if (NULL == hPfxCertStore && !*wzPFXPassword)
616 {
617 hPfxCertStore = ::PFXImportCertStore((CRYPT_DATA_BLOB*)&blob, NULL, dwKeyset);
618 }
619 ExitOnNullWithLastError(hPfxCertStore, hr, "Failed to open PFX file.");
620
621 // Find the first cert with a private key, or just use the last one
622 for (pCertContextEnum = ::CertEnumCertificatesInStore(hPfxCertStore, pCertContextEnum);
623 pCertContextEnum;
624 pCertContextEnum = ::CertEnumCertificatesInStore(hPfxCertStore, pCertContextEnum))
625 {
626 pCertContext = pCertContextEnum;
627
628 if (pCertContext && CertHasPrivateKey(pCertContext, NULL))
629 {
630 break;
631 }
632 }
633
634 ExitOnNullWithLastError(pCertContext, hr, "Failed to read first certificate out of PFX file.");
635
636 // Ignore failures, the worst that happens is some parts of the PFX get left behind.
637 CertReadProperty(pCertContext, CERT_KEY_PROV_INFO_PROP_ID, &pPfxInfo, NULL);
638 }
639 else
640 {
641 hr = E_UNEXPECTED;
642 ExitOnFailure(hr, "Unexpected certificate type processed.");
643 }
644 }
645
646 DWORD cb = cbHash;
647 if (!::CertGetCertificateContextProperty(pCertContext, CERT_SHA1_HASH_PROP_ID, static_cast<LPVOID>(rgbHash), &cb))
648 {
649 ExitWithLastError(hr, "Failed to get certificate SHA1 hash property.");
650 }
651 AssertSz(cb == cbHash, "Did not correctly read certificate SHA1 hash.");
652
653 LExit:
654 if (pCertContext)
655 {
656 ::CertFreeCertificateContext(pCertContext);
657 }
658
659 if (hPfxCertStore)
660 {
661 ::CertCloseStore(hPfxCertStore, 0);
662 }
663
664 if (pPfxInfo)
665 {
666 HCRYPTPROV hProvIgnored = NULL; // ignored on deletes.
667 ::CryptAcquireContextW(&hProvIgnored, pPfxInfo->pwszContainerName, pPfxInfo->pwszProvName, pPfxInfo->dwProvType, dwKeyset | CRYPT_DELETEKEYSET | CRYPT_SILENT);
668
669 MemFree(pPfxInfo);
670 }
671
672 return hr;
673 }
674
675
676 static HRESULT FindExistingCertificate(
677 __in LPCWSTR wzName,
678 __in DWORD dwStoreLocation,
679 __in LPCWSTR wzStore,
680 __out BYTE** prgbCertificate,
681 __out DWORD* pcbCertificate
682 )
683 {
684 HRESULT hr = S_OK;
685 HCERTSTORE hCertStore = NULL;
686 PCCERT_CONTEXT pCertContext = NULL;
687 BYTE* pbCertificate = NULL;
688 DWORD cbCertificate = 0;
689
690 hCertStore = ::CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, dwStoreLocation | CERT_STORE_READONLY_FLAG, wzStore);
691 MessageExitOnNullWithLastError(hCertStore, hr, msierrCERTFailedOpen, "Failed to open certificate store.");
692
693 // Loop through the certificate, looking for certificates that match our friendly name.
694 pCertContext = CertFindCertificateInStore(hCertStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL);
695 while (pCertContext)
696 {
697 WCHAR wzFriendlyName[256] = { 0 };
698 DWORD cbFriendlyName = sizeof(wzFriendlyName);
699
700 if (::CertGetCertificateContextProperty(pCertContext, CERT_FRIENDLY_NAME_PROP_ID, reinterpret_cast<BYTE*>(wzFriendlyName), &cbFriendlyName) &&
701 CSTR_EQUAL == ::CompareStringW(LOCALE_SYSTEM_DEFAULT, 0, wzName, -1, wzFriendlyName, -1))
702 {
703 // If the certificate with matching friendly name is valid, let's use that.
704 long lVerify = ::CertVerifyTimeValidity(NULL, pCertContext->pCertInfo);
705 if (0 == lVerify)
706 {
707 cbCertificate = pCertContext->cbCertEncoded;
708 pbCertificate = static_cast<BYTE*>(MemAlloc(cbCertificate, FALSE));
709 ExitOnNull(pbCertificate, hr, E_OUTOFMEMORY, "Failed to allocate memory to copy out exist certificate.");
710
711 CopyMemory(pbCertificate, pCertContext->pbCertEncoded, cbCertificate);
712 break; // found a matching certificate, no more searching necessary
713 }
714 }
715
716 // Next certificate in the store.
717 PCCERT_CONTEXT pNext = ::CertFindCertificateInStore(hCertStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, pCertContext);
718 // old pCertContext is freed by CertFindCertificateInStore
719 pCertContext = pNext;
720 }
721
722 *prgbCertificate = pbCertificate;
723 *pcbCertificate = cbCertificate;
724 pbCertificate = NULL;
725
726 LExit:
727 ReleaseMem(pbCertificate);
728
729 if (pCertContext)
730 {
731 ::CertFreeCertificateContext(pCertContext);
732 }
733
734 if (hCertStore)
735 {
736 ::CertCloseStore(hCertStore, 0);
737 }
738
739 return hr;
740 }
741
742 /*
743 HRESULT CreateEnroll(ICEnroll2 **hEnroll, INT iStore, INT iStoreLocation)
744 {
745 ICEnroll2 *pEnroll = NULL;
746 HRESULT hr = S_OK;
747 LONG lFlags;
748 DWORD dwFlags = iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT;
749
750 // create IEntroll
751 hr = CoCreateInstance( CLSID_CEnroll, NULL, CLSCTX_INPROC_SERVER, IID_ICEnroll2, (void **)&pEnroll );
752 if (FAILED(hr))
753 return hr;
754
755 switch (iStore)
756 {
757 case SCA_CERT_STORENAME_MY:
758 pEnroll->get_MyStoreFlags(&lFlags);
759 lFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK;
760 lFlags |= dwFlags;
761 // following call will change Request store flags also
762 pEnroll->put_MyStoreFlags(lFlags);
763 break;
764 case SCA_CERT_STORENAME_CA:
765 pEnroll->get_CAStoreFlags(&lFlags);
766 lFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK;
767 lFlags |= dwFlags;
768 // following call will change Request store flags also
769 pEnroll->put_CAStoreFlags(lFlags);
770 break;
771 case SCA_CERT_STORENAME_REQUEST:
772 pEnroll->get_RequestStoreFlags(&lFlags);
773 lFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK;
774 lFlags |= dwFlags;
775 // following call will change Request store flags also
776 pEnroll->put_RequestStoreFlags(lFlags);
777 break;
778 case SCA_CERT_STORENAME_ROOT:
779 pEnroll->get_RootStoreFlags(&lFlags);
780 lFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK;
781 lFlags |= dwFlags;
782 // following call will change Request store flags also
783 pEnroll->put_RootStoreFlags(lFlags);
784 break;
785 default:
786 hr = E_FAIL;
787 return hr;
788 }
789
790 pEnroll->get_GenKeyFlags(&lFlags);
791 lFlags |= CRYPT_EXPORTABLE;
792 pEnroll->put_GenKeyFlags(lFlags);
793
794 pEnroll->put_KeySpec(AT_KEYEXCHANGE);
795 pEnroll->put_ProviderType(PROV_RSA_SCHANNEL);
796 pEnroll->put_DeleteRequestCert(TRUE);
797
798 *hEnroll = pEnroll;
799 return hr;
800 }
801
802
803 HRESULT RequestCertificate(LPCWSTR pwzName, INT iStore, INT iStoreLocation,
804 LPCWSTR wzComputerName, LPCWSTR wzDistinguishedName, LPCWSTR wzCertificateAuthority,
805 BSTR *pbstrCertificate)
806 {
807 if (pbstrCertificate == NULL)
808 return E_INVALIDARG;
809
810 HRESULT hr;
811 ICEnroll2 *pEnroll = NULL;
812 ICertRequest *pCertRequest = NULL;
813 BSTR bstrRequest = NULL;
814 LONG nDisposition;
815
816 BSTR bstrCertificateUsage = NULL;
817 BSTR bstrCertificateAttributes = NULL;
818 BSTR bstrCertificateAuthority = NULL;
819
820 // equivalent to: sprintf(bstrDistinguishedName, L"%s,CN=%s", wzDistinguishedName, wzComputerName);
821 DWORD cchComputerName = lstrlenW(wzComputerName);
822 DWORD cchDistinguishedName = lstrlenW(wzDistinguishedName);
823 CONST DWORD cchbstrDistinguishedName = 5 + cchComputerName + cchDistinguishedName;
824 BSTR bstrDistinguishedName = SysAllocStringLen(NULL, cchbstrDistinguishedName);
825 ExitOnNull(bstrDistinguishedName, hr, E_OUTOFMEMORY, "Failed to allocate space for distinguished name.");
826 ::StringCchCopyW((WCHAR*) bstrDistinguishedName, cchbstrDistinguishedName, wzDistinguishedName);
827 ::StringCchCatW((WCHAR*) bstrDistinguishedName, cchbstrDistinguishedName, L",CN=");
828 ::StringCchCatW((WCHAR*) bstrDistinguishedName, cchbstrDistinguishedName, wzComputerName);
829
830 bstrCertificateUsage = SysAllocString(WIDE(szOID_PKIX_KP_SERVER_AUTH));
831 ExitOnNull(bstrCertificateUsage, hr, E_OUTOFMEMORY, "Failed to allocate space for Certificate Usage.");
832 bstrCertificateAttributes = SysAllocString(L"CertificateTemplate:WebServer");
833 bstrCertificateAuthority = SysAllocString(wzCertificateAuthority);
834 ExitOnNull(bstrCertificateAuthority, hr, E_OUTOFMEMORY, "Failed to allocate space for Certificate Authority.");
835
836 hr = CreateEnroll(&pEnroll, iStore, iStoreLocation);
837 ExitOnFailure(hr, "failed CoCreateInstance IEnroll");
838
839 hr = pEnroll->createPKCS10(bstrDistinguishedName, bstrCertificateUsage, &bstrRequest);
840 ExitOnFailure(hr, "failed createPKCS10");
841
842 hr = CoCreateInstance(CLSID_CCertRequest, NULL, CLSCTX_INPROC_SERVER, IID_ICertRequest, (void **)&pCertRequest);
843 ExitOnFailure(hr, "failed CoCreateInstance ICertRequest");
844
845 hr = pCertRequest->Submit(CR_IN_BASE64 | CR_IN_PKCS10, bstrRequest, bstrCertificateAttributes, bstrCertificateAuthority, &nDisposition);
846 ExitOnFailure(hr, "failed ICertRequest.Submit");
847
848 hr = (nDisposition == CR_DISP_ISSUED) ? S_OK : E_FAIL;
849 ExitOnFailure(hr, "failed CR_DISP_ISSUED");
850
851 hr = pCertRequest->GetCertificate(CR_OUT_BASE64, pbstrCertificate);
852 ExitOnFailure(hr, "failed ICertRequest.GetCertificate");
853
854 // save the certificate in place, cannot be passed to a deferred custom action
855 hr = pEnroll->acceptPKCS7(*pbstrCertificate);
856 ExitOnFailure(hr, "failed accept certificate into MY store");
857
858 LExit:
859 ReleaseObject(pCertRequest);
860 ReleaseBSTR(bstrRequest);
861 ReleaseObject(pEnroll);
862 ReleaseBSTR(bstrCertificateAuthority);
863 ReleaseBSTR(bstrCertificateAttributes);
864 ReleaseBSTR(bstrDistinguishedName);
865
866 return hr;
867 }
868
869
870 VOID ParseCertificateAuthority(__in LPCWSTR wzCertificateAuthorityOrig, __out LPWSTR *pwzBuffer, __out LPWSTR **hwzCAArray, __out int *piCAArray)
871 {
872 // @asAuthorities = split /;/, $sAuthority;
873 CONST WCHAR wchDelimiter = L';';
874
875 // copy constant into a buffer
876 Assert(wzCertificateAuthorityOrig);
877
878 INT cchCA = lstrlenW(wzCertificateAuthorityOrig) + 1;
879 WCHAR* wzBuffer = new WCHAR[cchCA];
880 if (!wzBuffer)
881 return;
882
883 ::StringCchCopyW(wzBuffer, cchCA, wzCertificateAuthorityOrig);
884
885 // determine the number of strings in the field
886 int iCAArray = 1;
887 int i;
888 for (i = 0; i < cchCA; ++i)
889 {
890 if (wzBuffer[i] == wchDelimiter)
891 ++iCAArray;
892 }
893 LPWSTR *pwzCAArray = (LPWSTR*) new BYTE[iCAArray * sizeof(LPWSTR)];
894 if (!pwzCAArray)
895 {
896 return;
897 }
898
899 pwzCAArray[0] = wzBuffer;
900 iCAArray = 0;
901 for (i = 0; i < cchCA; ++i)
902 {
903 if (wzBuffer[i] != wchDelimiter)
904 continue;
905 wzBuffer[i] = 0; // convert buffer into MULTISZ
906 pwzCAArray[iCAArray] = &wzBuffer[i+1];
907 ++iCAArray;
908 }
909
910 *pwzBuffer = wzBuffer;
911 *hwzCAArray = pwzCAArray;
912 *piCAArray = iCAArray;
913 }
914
915
916 HRESULT ScaSslExistingCertificateByBinaryData(INT iStore, INT iStoreLocation, BYTE* pwzData, DWORD cchData)
917 {
918 HRESULT hr = S_FALSE;
919 HCERTSTORE hCertStore = NULL;
920 PCCERT_CONTEXT pCertCtx = NULL, pCertCtxExisting = NULL;
921 DWORD dwFlags = 0;
922 LPCWSTR wzStore = StoreMapping(iStore);
923 CERT_BLOB blob;
924
925 dwFlags = iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT;
926 hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, dwFlags, wzStore);
927 MessageExitOnNullWithLastError(hCertStore, hr, msierrCERTFailedOpen, "failed to open certificate store, OK on uninstall");
928
929 blob.pbData = pwzData;
930 blob.cbData = cchData;
931
932 if (!::CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL,
933 0, NULL, NULL, NULL, NULL, NULL, (LPCVOID*)&pCertCtx))
934 ExitOnLastError(hr, "failed to parse the certificate blob, OK on uninstall");
935
936 pCertCtxExisting = CertFindCertificateInStore(
937 hCertStore,
938 PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
939 0,
940 CERT_FIND_EXISTING,
941 pCertCtx,
942 NULL);
943
944 if (pCertCtxExisting)
945 {
946 hr = S_OK;
947 }
948
949 LExit:
950 if (pCertCtx)
951 {
952 CertFreeCertificateContext(pCertCtx);
953 pCertCtx = NULL;
954 }
955 if (pCertCtxExisting)
956 {
957 CertFreeCertificateContext(pCertCtxExisting);
958 pCertCtxExisting = NULL;
959 }
960 if (hCertStore)
961 {
962 CertCloseStore(hCertStore, 0);
963 hCertStore = NULL;
964 }
965
966 return hr;
967 }
968
969
970 HRESULT ScaSslExistingCertificateByName(LPCWSTR pwzName, INT iStore, INT iStoreLocation,
971 BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer)
972 {
973 HRESULT hr = S_FALSE;
974 HCERTSTORE hSystemStore = NULL;
975 PCCERT_CONTEXT pTargetCert = NULL;
976 WCHAR wzFriendlyName[MAX_PATH] = {0};
977 DWORD dwFriendlyNameLen = sizeof(wzFriendlyName);
978
979 // Call CertOpenStore to open the CA store.
980 hSystemStore = CertOpenStore(
981 CERT_STORE_PROV_SYSTEM_REGISTRY,
982 0,
983 NULL,
984 (iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT) | CERT_STORE_OPEN_EXISTING_FLAG,
985 StoreMapping(iStore));
986 if (hSystemStore == NULL)
987 ExitFunction();
988
989 // Get a particular certificate using CertFindCertificateInStore.
990 pTargetCert = CertFindCertificateInStore(
991 hSystemStore,
992 PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
993 0,
994 CERT_FIND_ANY,
995 NULL,
996 NULL);
997 while (pTargetCert != NULL)
998 {
999 if ((CertGetCertificateContextProperty(pTargetCert, CERT_FRIENDLY_NAME_PROP_ID,
1000 (BYTE*)wzFriendlyName, &dwFriendlyNameLen)) &&
1001 lstrcmpW(wzFriendlyName, pwzName) == 0)
1002 {
1003 // pTargetCert is a pointer to the desired certificate.
1004 // Check the certificate's validity.
1005 switch (CertVerifyTimeValidity(
1006 NULL,
1007 pTargetCert->pCertInfo))
1008 {
1009 case 1:
1010 // Certificate is expired
1011 WcaLog(LOGMSG_STANDARD, "The SSL certificate has expired");
1012 // always remove it
1013 {
1014 PCCERT_CONTEXT pDupCertContext = CertDuplicateCertificateContext(pTargetCert);
1015 if (pDupCertContext && CertDeleteCertificateFromStore(pDupCertContext))
1016 {
1017 WcaLog(LOGMSG_STANDARD, "A SSL certificate has removed");
1018 }
1019 }
1020 break;
1021 case 0:
1022 // Certificate is valid
1023 WcaLog(LOGMSG_STANDARD, "The SSL certificate is valid");
1024 hr = S_OK;
1025 if (pbaHashBuffer)
1026 {
1027 // if the certificate already exists and is valid, use that one
1028 DWORD dwHashSize = CB_CERTIFICATE_HASH;
1029 hr = CertGetCertificateContextProperty(pTargetCert, CERT_SHA1_HASH_PROP_ID, (VOID*)pbaHashBuffer, &dwHashSize)
1030 ? S_OK : E_FAIL;
1031 ExitOnFailure(hr, "failed CertGetCertificateContextProperty CERT_SHA1_HASH_PROP_ID");
1032 Assert(pbstrCertificate);
1033 Assert(pcbCertificate);
1034 ReleaseBSTR(*pbstrCertificate);
1035
1036 *pbstrCertificate = SysAllocStringByteLen((LPCSTR)(pTargetCert->pbCertEncoded), pTargetCert->cbCertEncoded);
1037 *pcbCertificate = pTargetCert->cbCertEncoded;
1038 }
1039 ExitFunction();
1040 break;
1041 default:
1042 // Certificate not valid yet, ignore it
1043 WcaLog(LOGMSG_STANDARD, "The SSL certificate is not valid");
1044 break;
1045 }
1046 }
1047 pTargetCert = CertFindCertificateInStore(
1048 hSystemStore,
1049 PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
1050 0,
1051 CERT_FIND_ANY,
1052 NULL,
1053 pTargetCert);
1054 wzFriendlyName[0] = 0;
1055 dwFriendlyNameLen = sizeof(wzFriendlyName);
1056 }
1057
1058 LExit:
1059 // Clean up memory and quit.
1060 if (pTargetCert)
1061 {
1062 CertFreeCertificateContext(pTargetCert);
1063 pTargetCert = NULL;
1064 }
1065 if (hSystemStore)
1066 {
1067 CertCloseStore(hSystemStore, CERT_CLOSE_STORE_CHECK_FLAG);
1068 hSystemStore = NULL;
1069 }
1070
1071 return hr;
1072 }
1073
1074
1075 HRESULT ScaSslNewCertificate(LPCWSTR pwzName, INT iStore, INT iStoreLocation, LPCWSTR wzComputerName, LPCWSTR wzDistinguishedName, LPCWSTR wzCertificateAuthorityOrig,
1076 BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer)
1077 {
1078
1079 if (pbstrCertificate == NULL)
1080 return E_INVALIDARG;
1081
1082 HRESULT hr = S_OK;
1083 LPWSTR wzCABuffer = NULL;
1084 LPWSTR *wzCAArray = NULL;
1085 int iCAArray = 0;
1086
1087 // otherwise call the CA for one
1088 ParseCertificateAuthority(wzCertificateAuthorityOrig, &wzCABuffer, &wzCAArray, &iCAArray);
1089
1090 // try each authority three times
1091 for (int i = 0; i < 3 * iCAArray; ++i)
1092 {
1093 LPCWSTR wzCA = wzCAArray[i % iCAArray];
1094 if (NULL == wzCA || NULL == wzCA[0]) continue;
1095 WcaLog(LOGMSG_STANDARD, "Requesting SSL certificate from %ls", wzCA);
1096 hr = RequestCertificate(pwzName, iStore, iStoreLocation, wzComputerName, wzDistinguishedName, wzCA, pbstrCertificate);
1097 if (hr == S_OK && pbstrCertificate)
1098 {
1099 // set the friendly name
1100 CRYPT_HASH_BLOB hblob;
1101 CERT_BLOB blob;
1102 HCERTSTORE hCertStore = NULL;
1103 PCCERT_CONTEXT pCertCtxExisting = NULL;
1104
1105 blob.pbData = (BYTE*)pwzName;
1106 blob.cbData = (lstrlenW(pwzName) + 1) * sizeof(pwzName[0]); // including terminating null
1107
1108 *pcbCertificate = SysStringByteLen(*pbstrCertificate);
1109 hr = CertificateToHash(*pbstrCertificate, pbaHashBuffer);
1110 ExitOnFailure(hr, "failed to CertificateToHash for an existing certificate");
1111
1112 hblob.pbData = pbaHashBuffer;
1113 hblob.cbData = CB_CERTIFICATE_HASH;
1114
1115 hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, (iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT), StoreMapping(iStore));
1116 MessageExitOnNullWithLastError(hCertStore, hr, msierrCERTFailedOpen, "failed to open certificate store");
1117
1118 pCertCtxExisting = CertFindCertificateInStore(
1119 hCertStore,
1120 PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
1121 0,
1122 CERT_FIND_HASH,
1123 &hblob,
1124 NULL);
1125
1126 if (pCertCtxExisting)
1127 {
1128 CertSetCertificateContextProperty(
1129 pCertCtxExisting,
1130 CERT_FRIENDLY_NAME_PROP_ID,
1131 0,
1132 &blob);
1133 }
1134
1135 if (pCertCtxExisting)
1136 {
1137 CertFreeCertificateContext(pCertCtxExisting);
1138 pCertCtxExisting = NULL;
1139 }
1140 if (hCertStore)
1141 {
1142 CertCloseStore(hCertStore, 0);
1143 hCertStore = NULL;
1144 }
1145 ExitFunction();
1146 }
1147 if (pbstrCertificate && *pbstrCertificate)
1148 {
1149 SysFreeString(*pbstrCertificate);
1150 pbstrCertificate = NULL;
1151 }
1152 }
1153 hr = E_FAIL;
1154 ExitOnFailure(hr, "failed to RequestCertificate");
1155
1156 LExit:
1157 if (wzCABuffer)
1158 {
1159 delete wzCABuffer;
1160 }
1161 if (wzCAArray)
1162 {
1163 delete wzCAArray;
1164 }
1165
1166 return hr;
1167 }
1168
1169
1170 HRESULT ScaGetCertificateByRequest(LPCWSTR pwzName, BOOL fIsInstalling, BOOL fIsUninstalling,
1171 INT iStore, INT iStoreLocation,
1172 LPCWSTR wzDistinguishedName, LPCWSTR wzCA,
1173 BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer)
1174 {
1175 HRESULT hr = S_OK;
1176 WCHAR wzComputerName[MAX_COMPUTER_NAME] = {0};
1177 WCHAR* pwzData = NULL;
1178 DWORD cchData = 0;
1179
1180 // override %COMPUTERNAME% with DOMAINNAME property
1181 hr = WcaGetProperty( L"DOMAINNAME", &pwzData);
1182 ExitOnFailure(hr, "Failed to get Property DOMAINNAME");
1183 if (*pwzData)
1184 {
1185 // if DOMAINNAME is set, use it
1186 ::StringCchCopyW(wzComputerName, MAX_COMPUTER_NAME, pwzData);
1187 }
1188 else
1189 {
1190 // otherwise get the intranet name given by %COMPUTERNAME%
1191 GetEnvironmentVariableW(L"COMPUTERNAME", wzComputerName, MAX_COMPUTER_NAME);
1192 }
1193
1194 hr = ScaSslExistingCertificateByName(pwzName, iStore, iStoreLocation, pbstrCertificate, pcbCertificate, pbaHashBuffer);
1195 ExitOnFailure(hr, "Failed ScaSslExistingCertificateByName");
1196 if (S_OK != hr)
1197 {
1198 if (!fIsUninstalling && fIsInstalling)
1199 {
1200 // if no existing cert and not on uninstall, hit the authority
1201 WcaLog(LOGMSG_STANDARD, "Adding certificate: requested, %ls", wzDistinguishedName);
1202 hr = ScaSslNewCertificate(pwzName, iStore, iStoreLocation, wzComputerName, wzDistinguishedName, wzCA,
1203 pbstrCertificate, pcbCertificate, pbaHashBuffer);
1204 ExitOnFailure(hr, "Failed ScaSslNewCertificate");
1205 }
1206 else
1207 {
1208 // if no existing cert and uninstall
1209 hr = S_OK;
1210 }
1211 }
1212
1213 LExit:
1214 ReleaseStr(pwzData);
1215
1216 return hr;
1217 }
1218
1219
1220 HRESULT ScaInstallCertificateByContext(LPCWSTR pwzName, INT iStore, INT iStoreLocation,
1221 PCCERT_CONTEXT pCertContext)
1222 {
1223 HRESULT hr = S_OK;
1224 HCERTSTORE hCertStore = NULL;
1225 DWORD dwFlags = iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT;
1226 CERT_BLOB blob;
1227
1228 hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, dwFlags, StoreMapping(iStore));
1229 if (hCertStore == NULL)
1230 MessageExitOnLastError(hr, msierrCERTFailedOpen, "failed to open certificate store");
1231
1232 blob.pbData = (BYTE*)pwzName;
1233 blob.cbData = (lstrlenW(pwzName) + 1) * sizeof(pwzName[0]); // including terminating null
1234 CertSetCertificateContextProperty(
1235 pCertContext,
1236 CERT_FRIENDLY_NAME_PROP_ID,
1237 0,
1238 &blob);
1239
1240 if (!CertAddCertificateContextToStore(
1241 hCertStore,
1242 pCertContext,
1243 CERT_STORE_ADD_REPLACE_EXISTING,
1244 NULL))
1245 {
1246 hr = E_FAIL;
1247 MessageExitOnLastError(hr, msierrCERTFailedAdd, "failed to add certificate to the store");
1248 }
1249
1250 LExit:
1251 if (hCertStore)
1252 {
1253 CertCloseStore(hCertStore, 0);
1254 hCertStore = NULL;
1255 }
1256
1257 return hr;
1258 }
1259
1260
1261 HRESULT ScaGetCertificateByPath(LPCWSTR pwzName, BOOL fIsInstalling, BOOL fIsUninstalling,
1262 INT iStore, INT iStoreLocation, LPCWSTR wzSslCertificate, LPCWSTR wzPFXPassword,
1263 BSTR* pbstrCertificate, DWORD* pcbCertificate, BYTE* pbaHashBuffer)
1264 {
1265 Assert(wzSslCertificate);
1266 HRESULT hr = S_OK;
1267 PCCERT_CONTEXT pCertContext = NULL;
1268 DWORD dwEncodingType = 0;
1269 DWORD dwContentType = 0;
1270 DWORD dwFormatType = 0;
1271 DWORD dwHashSize = CB_CERTIFICATE_HASH;
1272 HANDLE hPfxFile = INVALID_HANDLE_VALUE;
1273 CRYPT_DATA_BLOB blob;
1274
1275 blob.pbData = NULL;
1276 blob.cbData = 0;
1277
1278 if (wzSslCertificate && wzSslCertificate[0] != 0)
1279 {
1280 if (!::CryptQueryObject(CERT_QUERY_OBJECT_FILE, (LPVOID)wzSslCertificate, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL,
1281 0, &dwEncodingType, &dwContentType, &dwFormatType, NULL, NULL, (LPCVOID*)&pCertContext))
1282 hr = fIsUninstalling ? S_FALSE : HRESULT_FROM_WIN32(::GetLastError()); // don't fail on uninstall
1283 ExitOnFailure(hr, "failed CryptQueryObject");
1284 }
1285 else
1286 {
1287 hr = S_FALSE;
1288 ExitFunction();
1289 }
1290
1291 if (!pCertContext)
1292 {
1293 // this is a pfx?
1294 // make sure to exit this block of code properly for clean up blob.pbData
1295 if (dwContentType & CERT_QUERY_CONTENT_PFX)
1296 {
1297 DWORD iSize = 0, iReadSize = 0;
1298 HCERTSTORE hPfxCertStore = NULL;
1299
1300 hPfxFile = ::CreateFileW(wzSslCertificate, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1301 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1302 hr = (hPfxFile != INVALID_HANDLE_VALUE) ? S_OK : E_FAIL;
1303 ExitOnFailure(hr, "failed CryptQueryObject, file handle is null");
1304 iSize = ::GetFileSize(hPfxFile, NULL);
1305 hr = (iSize > 0) ? S_OK : E_FAIL;
1306 ExitOnFailure(hr, "failed CryptQueryObject, file size is 0");
1307 blob.pbData = new BYTE[iSize];
1308 blob.cbData = iSize;
1309 hr = (blob.pbData) ? S_OK : E_FAIL;
1310 ExitOnFailure(hr, "out of memory for blob");
1311
1312 if (::ReadFile(hPfxFile, (LPVOID)blob.pbData, iSize, &iReadSize, NULL))
1313 {
1314 hPfxCertStore = PFXImportCertStore((CRYPT_DATA_BLOB*)&blob, wzPFXPassword,
1315 (iStoreLocation == SCA_CERTSYSTEMSTORE_CURRENTUSER) ? CRYPT_USER_KEYSET : CRYPT_MACHINE_KEYSET);
1316 if (hPfxCertStore)
1317 {
1318 pCertContext = CertEnumCertificatesInStore(hPfxCertStore, NULL);
1319 // work only with the first certificate in pfx
1320 if (pCertContext)
1321 {
1322 hr = CertGetCertificateContextProperty(pCertContext, CERT_SHA1_HASH_PROP_ID, (VOID*)pbaHashBuffer, &dwHashSize)
1323 ? S_OK : E_FAIL;
1324 ExitOnFailure(hr, "failed CertGetCertificateContextProperty CERT_SHA1_HASH_PROP_ID");
1325 ReleaseBSTR(*pbstrCertificate);
1326
1327 *pbstrCertificate = SysAllocStringByteLen((LPCSTR)(pCertContext->pbCertEncoded), pCertContext->cbCertEncoded);
1328 *pcbCertificate = pCertContext->cbCertEncoded;
1329 if (fIsInstalling)
1330 {
1331 // install the certificate, cannot defer because the data required cannot be passed
1332 hr = ScaInstallCertificateByContext(pwzName, iStore, iStoreLocation, pCertContext);
1333 }
1334 }
1335 else
1336 hr = E_FAIL;
1337 }
1338 else
1339 hr = E_FAIL;
1340 }
1341 else
1342 hr = E_FAIL;
1343 }
1344 else
1345 {
1346 ExitOnFailure(hr = E_FAIL, "failed CryptQueryObject, unknown data");
1347 }
1348 }
1349 else
1350 {
1351 // return cert and its hash
1352 hr = CertGetCertificateContextProperty(pCertContext, CERT_SHA1_HASH_PROP_ID, (VOID*)pbaHashBuffer, &dwHashSize)
1353 ? S_OK : E_FAIL;
1354 ExitOnFailure(hr, "failed CertGetCertificateContextProperty CERT_SHA1_HASH_PROP_ID");
1355 ReleaseBSTR(*pbstrCertificate);
1356
1357 *pbstrCertificate = SysAllocStringByteLen((LPCSTR)(pCertContext->pbCertEncoded), pCertContext->cbCertEncoded);
1358 *pcbCertificate = pCertContext->cbCertEncoded;
1359 if (fIsInstalling)
1360 {
1361 // install the certificate, cannot defer because the data required cannot be passed
1362 hr = ScaInstallCertificateByContext(pwzName, iStore, iStoreLocation, pCertContext);
1363 }
1364 }
1365
1366 LExit:
1367 if (pCertContext)
1368 {
1369 CertFreeCertificateContext(pCertContext);
1370 pCertContext = NULL;
1371 }
1372 if (hPfxFile != INVALID_HANDLE_VALUE)
1373 {
1374 CloseHandle(hPfxFile);
1375 hPfxFile = INVALID_HANDLE_VALUE;
1376 }
1377 if (blob.pbData)
1378 {
1379 delete [] blob.pbData;
1380 blob.pbData = NULL;
1381 blob.cbData = 0;
1382 }
1383
1384 return hr;
1385 }
1386
1387
1388 HRESULT ScaInstallCertificateByBinaryData(BOOL fAddCert, INT iStore, INT iStoreLocation, LPCWSTR wzName, BYTE* pwzData, DWORD cchData,
1389 LPCWSTR wzPFXPassword)
1390 {
1391 Assert(wzName);
1392 Assert(pwzData);
1393 Assert(cchData);
1394 HRESULT hr = S_OK;
1395 HCERTSTORE hCertStore = NULL, hPfxCertStore = NULL;
1396 PCCERT_CONTEXT pCertCtx = NULL, pCertCtxExisting = NULL;
1397 DWORD dwFlags, dwEncodingType, dwContentType, dwFormatType;
1398 CERT_BLOB blob;
1399 LPCWSTR wzStore = StoreMapping(iStore);
1400
1401 dwFlags = iStoreLocation << CERT_SYSTEM_STORE_LOCATION_SHIFT;
1402 hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, dwFlags, wzStore);
1403 MessageExitOnNullWithLastError(hCertStore, hr, msierrCERTFailedOpen, "failed to open certificate store");
1404
1405 blob.pbData = pwzData;
1406 blob.cbData = cchData;
1407
1408 if (!::CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL,
1409 0, &dwEncodingType, &dwContentType, &dwFormatType, NULL, NULL, (LPCVOID*)&pCertCtx))
1410 ExitOnLastError(hr, "failed to parse the certificate blob");
1411 ExitOnNull(pCertCtx, hr, E_UNEXPECTED, "failed to parse the certificate blob");
1412
1413 blob.pbData = (BYTE*)wzName;
1414 blob.cbData = (lstrlenW(wzName) + 1) * sizeof(wzName[0]); // including terminating null
1415
1416 CertSetCertificateContextProperty(
1417 pCertCtx,
1418 CERT_FRIENDLY_NAME_PROP_ID,
1419 0,
1420 &blob);
1421
1422 if (fAddCert)
1423 {
1424 // Add
1425 WcaLog(LOGMSG_STANDARD, "Adding certificate: binary name, %ls", wzName);
1426 if (!CertAddCertificateContextToStore(
1427 hCertStore,
1428 pCertCtx,
1429 CERT_STORE_ADD_REPLACE_EXISTING,
1430 NULL))
1431 {
1432 hr = E_FAIL;
1433 MessageExitOnLastError(hr, msierrCERTFailedAdd, "failed to add certificate to the store");
1434 }
1435 }
1436 else
1437 {
1438 // Delete
1439 WcaLog(LOGMSG_STANDARD, "Deleting certificate provided: binary name, %ls", wzName);
1440 pCertCtxExisting = CertFindCertificateInStore(
1441 hCertStore,
1442 PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
1443 0,
1444 CERT_FIND_EXISTING,
1445 pCertCtx,
1446 NULL);
1447
1448 if (pCertCtxExisting)
1449 {
1450 if (!CertDeleteCertificateFromStore(pCertCtxExisting))
1451 {
1452 ExitOnLastError(hr, "failed to delete certificate");
1453 }
1454 else
1455 {
1456 pCertCtxExisting = NULL;
1457 }
1458 }
1459 }
1460
1461 LExit:
1462 if (pCertCtx)
1463 {
1464 CertFreeCertificateContext(pCertCtx);
1465 pCertCtx = NULL;
1466 }
1467 if (pCertCtxExisting)
1468 {
1469 CertFreeCertificateContext(pCertCtxExisting);
1470 pCertCtxExisting = NULL;
1471 }
1472 // order is important for store
1473 if (hCertStore)
1474 {
1475 CertCloseStore(hCertStore, 0);
1476 hCertStore = NULL;
1477 }
1478 if (hPfxCertStore)
1479 {
1480 CertCloseStore(hPfxCertStore, 0);
1481 hPfxCertStore = NULL;
1482 }
1483
1484 return hr;
1485 }
1486 */