aboutsummaryrefslogtreecommitdiff
path: root/src/ca/scacert.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ca/scacert.cpp')
-rw-r--r--src/ca/scacert.cpp1479
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
6static HRESULT ConfigureCertificates(
7 __in SCA_ACTION saAction
8 );
9
10static LPCWSTR StoreMapping(
11 __in int iStore
12 );
13
14static HRESULT FindExistingCertificate(
15 __in LPCWSTR wzName,
16 __in DWORD dwStoreLocation,
17 __in LPCWSTR wzStore,
18 __out BYTE** prgbCertificate,
19 __out DWORD* pcbCertificate
20 );
21
22static 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
34static HRESULT ReadCertificateFile(
35 __in LPCWSTR wzPath,
36 __out BYTE** prgbData,
37 __out DWORD* pcbData
38 );
39
40static 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/*
50HRESULT 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
56HRESULT 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
62HRESULT ScaSslNewCertificate(LPCWSTR pwzName, INT iStore,
63 INT iStoreLocation, LPCWSTR wzComputerName,
64 LPCWSTR wzDistinguishedName, LPCWSTR wzCertificateAuthorityOrig,
65 BSTR* pbstrCertificate, DWORD* pcbCertificate,
66 BYTE* pbaHashBuffer);
67
68HRESULT ScaSslExistingCertificateByName(LPCWSTR pwzName, INT iStore,
69 INT iStoreLocation, BSTR* pbstrCertificate,
70 DWORD* pcbCertificate, BYTE* pbaHashBuffer);
71
72HRESULT ScaSslExistingCertificateByBinaryData(INT iStore, INT iStoreLocation,
73 BYTE* pwzData, DWORD cchData);
74
75HRESULT CreateEnroll(ICEnroll2 **hEnroll, INT iStore,
76 INT iStoreLocation);
77
78HRESULT RequestCertificate(LPCWSTR pwzName, INT iStore,
79 INT iStoreLocation, LPCWSTR wzComputerName,
80 LPCWSTR wzDistinguishedName, LPCWSTR wzCertificateAuthority,
81 BSTR *pbstrCertificate);
82
83VOID ParseCertificateAuthority(__in LPCWSTR wzCertificateAuthorityOrig, __out LPWSTR *pwzBuffer,
84 __out LPWSTR **hwzCAArray, __out int *piCAArray);
85*/
86
87
88LPCWSTR vcsCertQuery = L"SELECT `Certificate`, `Name`, `Component_`, `StoreLocation`, `StoreName`, `Attributes`, `Binary_`, `CertificatePath`, `PFXPassword` FROM `Certificate`";
89enum eCertQuery { cqCertificate = 1, cqName, cqComponent, cqStoreLocation, cqStoreName, cqAttributes, cqCertificateBinary, cqCertificatePath, cqPFXPassword };
90
91
92/********************************************************************
93InstallCertificates - CUSTOM ACTION ENTRY POINT for installing
94 certificates
95
96********************************************************************/
97extern "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
110LExit:
111 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
112 return WcaFinalize(er);
113}
114
115
116/********************************************************************
117UninstallCertificates - CUSTOM ACTION ENTRY POINT for uninstalling
118 certificates
119
120********************************************************************/
121extern "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
134LExit:
135 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
136 return WcaFinalize(er);
137}
138
139
140static 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
336LExit:
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
355static 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
501LExit:
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
521static 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
566LExit:
567 ReleaseMem(pbData);
568 return hr;
569}
570
571
572static 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
646LExit:
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
669static 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
719LExit:
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/*
736HRESULT 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
796HRESULT 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
851LExit:
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
863VOID 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
909HRESULT 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
942LExit:
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
963HRESULT 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
1051LExit:
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
1068HRESULT 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
1149LExit:
1150 if (wzCABuffer)
1151 {
1152 delete wzCABuffer;
1153 }
1154 if (wzCAArray)
1155 {
1156 delete wzCAArray;
1157 }
1158
1159 return hr;
1160}
1161
1162
1163HRESULT 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
1206LExit:
1207 ReleaseStr(pwzData);
1208
1209 return hr;
1210}
1211
1212
1213HRESULT 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
1243LExit:
1244 if (hCertStore)
1245 {
1246 CertCloseStore(hCertStore, 0);
1247 hCertStore = NULL;
1248 }
1249
1250 return hr;
1251}
1252
1253
1254HRESULT 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
1359LExit:
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
1381HRESULT 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
1454LExit:
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*/