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