bb5228286f626b44641239bf22acd66515846dd6
[reactos.git] / dll / win32 / wintrust / wintrust_main.c
1 /*
2 * Copyright 2001 Rein Klazes
3 * Copyright 2007 Juan Lang
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19
20 #include "config.h"
21
22 #include <stdarg.h>
23
24 #define NONAMELESSUNION
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winerror.h"
29 #include "winreg.h"
30 #include "guiddef.h"
31 #include "wintrust.h"
32 #include "softpub.h"
33 #include "mscat.h"
34 #include "objbase.h"
35 #include "winuser.h"
36 #include "cryptdlg.h"
37 #include "cryptuiapi.h"
38 #include "wintrust_priv.h"
39 #include "wine/debug.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(wintrust);
42
43
44 /* Utility functions */
45 void * WINAPI WINTRUST_Alloc(DWORD cb)
46 {
47 return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cb);
48 }
49
50 static void* WINTRUST_ReAlloc(void *ptr, DWORD cb) __WINE_ALLOC_SIZE(2);
51 static void* WINTRUST_ReAlloc(void *ptr, DWORD cb)
52 {
53 return HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptr, cb);
54 }
55
56 void WINAPI WINTRUST_Free(void *p)
57 {
58 HeapFree(GetProcessHeap(), 0, p);
59 }
60
61 /***********************************************************************
62 * TrustIsCertificateSelfSigned (WINTRUST.@)
63 */
64 BOOL WINAPI TrustIsCertificateSelfSigned( PCCERT_CONTEXT cert )
65 {
66 PCERT_EXTENSION ext;
67 DWORD size;
68 BOOL ret;
69
70 TRACE("%p\n", cert);
71 if ((ext = CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER2,
72 cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
73 {
74 CERT_AUTHORITY_KEY_ID2_INFO *info;
75
76 ret = CryptDecodeObjectEx(cert->dwCertEncodingType,
77 X509_AUTHORITY_KEY_ID2, ext->Value.pbData, ext->Value.cbData,
78 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
79 &info, &size);
80 if (ret)
81 {
82 if (info->AuthorityCertIssuer.cAltEntry &&
83 info->AuthorityCertSerialNumber.cbData)
84 {
85 PCERT_ALT_NAME_ENTRY directoryName = NULL;
86 DWORD i;
87
88 for (i = 0; !directoryName &&
89 i < info->AuthorityCertIssuer.cAltEntry; i++)
90 if (info->AuthorityCertIssuer.rgAltEntry[i].dwAltNameChoice
91 == CERT_ALT_NAME_DIRECTORY_NAME)
92 directoryName =
93 &info->AuthorityCertIssuer.rgAltEntry[i];
94 if (directoryName)
95 {
96 ret = CertCompareCertificateName(cert->dwCertEncodingType,
97 &directoryName->u.DirectoryName, &cert->pCertInfo->Issuer)
98 && CertCompareIntegerBlob(&info->AuthorityCertSerialNumber,
99 &cert->pCertInfo->SerialNumber);
100 }
101 else
102 {
103 FIXME("no supported name type in authority key id2\n");
104 ret = FALSE;
105 }
106 }
107 else if (info->KeyId.cbData)
108 {
109 ret = CertGetCertificateContextProperty(cert,
110 CERT_KEY_IDENTIFIER_PROP_ID, NULL, &size);
111 if (ret && size == info->KeyId.cbData)
112 {
113 LPBYTE buf = CryptMemAlloc(size);
114
115 if (buf)
116 {
117 CertGetCertificateContextProperty(cert,
118 CERT_KEY_IDENTIFIER_PROP_ID, buf, &size);
119 ret = !memcmp(buf, info->KeyId.pbData, size);
120 CryptMemFree(buf);
121 }
122 else
123 ret = FALSE;
124 }
125 else
126 ret = FALSE;
127 }
128 LocalFree(info);
129 }
130 }
131 else if ((ext = CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER,
132 cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
133 {
134 CERT_AUTHORITY_KEY_ID_INFO *info;
135
136 ret = CryptDecodeObjectEx(cert->dwCertEncodingType,
137 X509_AUTHORITY_KEY_ID, ext->Value.pbData, ext->Value.cbData,
138 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
139 &info, &size);
140 if (ret)
141 {
142 if (info->CertIssuer.cbData && info->CertSerialNumber.cbData)
143 {
144 ret = CertCompareCertificateName(cert->dwCertEncodingType,
145 &info->CertIssuer, &cert->pCertInfo->Issuer) &&
146 CertCompareIntegerBlob(&info->CertSerialNumber,
147 &cert->pCertInfo->SerialNumber);
148 }
149 else if (info->KeyId.cbData)
150 {
151 ret = CertGetCertificateContextProperty(cert,
152 CERT_KEY_IDENTIFIER_PROP_ID, NULL, &size);
153 if (ret && size == info->KeyId.cbData)
154 {
155 LPBYTE buf = CryptMemAlloc(size);
156
157 if (buf)
158 {
159 CertGetCertificateContextProperty(cert,
160 CERT_KEY_IDENTIFIER_PROP_ID, buf, &size);
161 ret = !memcmp(buf, info->KeyId.pbData, size);
162 CryptMemFree(buf);
163 }
164 else
165 ret = FALSE;
166 }
167 else
168 ret = FALSE;
169 }
170 else
171 ret = FALSE;
172 LocalFree(info);
173 }
174 }
175 else
176 ret = CertCompareCertificateName(cert->dwCertEncodingType,
177 &cert->pCertInfo->Subject, &cert->pCertInfo->Issuer);
178 return ret;
179 }
180
181 typedef HRESULT (WINAPI *wintrust_step_func)(CRYPT_PROVIDER_DATA *data);
182
183 struct wintrust_step
184 {
185 wintrust_step_func func;
186 DWORD error_index;
187 };
188
189 static DWORD WINTRUST_ExecuteSteps(const struct wintrust_step *steps,
190 DWORD numSteps, CRYPT_PROVIDER_DATA *provData)
191 {
192 DWORD i, err = ERROR_SUCCESS;
193
194 for (i = 0; !err && i < numSteps; i++)
195 {
196 err = steps[i].func(provData);
197 if (err)
198 err = provData->padwTrustStepErrors[steps[i].error_index];
199 }
200 return err;
201 }
202
203 static CRYPT_PROVIDER_DATA *WINTRUST_AllocateProviderData(void)
204 {
205 CRYPT_PROVIDER_DATA *provData;
206
207 provData = WINTRUST_Alloc(sizeof(CRYPT_PROVIDER_DATA));
208 if (!provData)
209 goto oom;
210 provData->cbStruct = sizeof(CRYPT_PROVIDER_DATA);
211
212 provData->padwTrustStepErrors =
213 WINTRUST_Alloc(TRUSTERROR_MAX_STEPS * sizeof(DWORD));
214 if (!provData->padwTrustStepErrors)
215 goto oom;
216 provData->cdwTrustStepErrors = TRUSTERROR_MAX_STEPS;
217
218 provData->u.pPDSip = WINTRUST_Alloc(sizeof(PROVDATA_SIP));
219 if (!provData->u.pPDSip)
220 goto oom;
221 provData->u.pPDSip->cbStruct = sizeof(PROVDATA_SIP);
222
223 provData->psPfns = WINTRUST_Alloc(sizeof(CRYPT_PROVIDER_FUNCTIONS));
224 if (!provData->psPfns)
225 goto oom;
226 provData->psPfns->cbStruct = sizeof(CRYPT_PROVIDER_FUNCTIONS);
227 return provData;
228
229 oom:
230 if (provData)
231 {
232 WINTRUST_Free(provData->padwTrustStepErrors);
233 WINTRUST_Free(provData->u.pPDSip);
234 WINTRUST_Free(provData->psPfns);
235 WINTRUST_Free(provData);
236 }
237 return NULL;
238 }
239
240 /* Adds trust steps for each function in psPfns. Assumes steps has at least
241 * 5 entries. Returns the number of steps added.
242 */
243 static DWORD WINTRUST_AddTrustStepsFromFunctions(struct wintrust_step *steps,
244 const CRYPT_PROVIDER_FUNCTIONS *psPfns)
245 {
246 DWORD numSteps = 0;
247
248 if (psPfns->pfnInitialize)
249 {
250 steps[numSteps].func = psPfns->pfnInitialize;
251 steps[numSteps++].error_index = TRUSTERROR_STEP_FINAL_WVTINIT;
252 }
253 if (psPfns->pfnObjectTrust)
254 {
255 steps[numSteps].func = psPfns->pfnObjectTrust;
256 steps[numSteps++].error_index = TRUSTERROR_STEP_FINAL_OBJPROV;
257 }
258 if (psPfns->pfnSignatureTrust)
259 {
260 steps[numSteps].func = psPfns->pfnSignatureTrust;
261 steps[numSteps++].error_index = TRUSTERROR_STEP_FINAL_SIGPROV;
262 }
263 if (psPfns->pfnCertificateTrust)
264 {
265 steps[numSteps].func = psPfns->pfnCertificateTrust;
266 steps[numSteps++].error_index = TRUSTERROR_STEP_FINAL_CERTPROV;
267 }
268 if (psPfns->pfnFinalPolicy)
269 {
270 steps[numSteps].func = psPfns->pfnFinalPolicy;
271 steps[numSteps++].error_index = TRUSTERROR_STEP_FINAL_POLICYPROV;
272 }
273 return numSteps;
274 }
275
276 static LONG WINTRUST_DefaultVerify(HWND hwnd, GUID *actionID,
277 WINTRUST_DATA *data)
278 {
279 DWORD err = ERROR_SUCCESS, numSteps = 0;
280 CRYPT_PROVIDER_DATA *provData;
281 BOOL ret;
282 struct wintrust_step verifySteps[5];
283
284 TRACE("(%p, %s, %p)\n", hwnd, debugstr_guid(actionID), data);
285
286 provData = WINTRUST_AllocateProviderData();
287 if (!provData)
288 return ERROR_OUTOFMEMORY;
289
290 ret = WintrustLoadFunctionPointers(actionID, provData->psPfns);
291 if (!ret)
292 {
293 err = GetLastError();
294 goto error;
295 }
296
297 data->hWVTStateData = provData;
298 provData->pWintrustData = data;
299 if (hwnd == INVALID_HANDLE_VALUE)
300 provData->hWndParent = GetDesktopWindow();
301 else
302 provData->hWndParent = hwnd;
303 provData->pgActionID = actionID;
304 WintrustGetRegPolicyFlags(&provData->dwRegPolicySettings);
305
306 numSteps = WINTRUST_AddTrustStepsFromFunctions(verifySteps,
307 provData->psPfns);
308 err = WINTRUST_ExecuteSteps(verifySteps, numSteps, provData);
309 goto done;
310
311 error:
312 if (provData)
313 {
314 WINTRUST_Free(provData->padwTrustStepErrors);
315 WINTRUST_Free(provData->u.pPDSip);
316 WINTRUST_Free(provData->psPfns);
317 WINTRUST_Free(provData);
318 }
319 done:
320 TRACE("returning %08x\n", err);
321 return err;
322 }
323
324 static LONG WINTRUST_DefaultClose(HWND hwnd, GUID *actionID,
325 WINTRUST_DATA *data)
326 {
327 DWORD err = ERROR_SUCCESS;
328 CRYPT_PROVIDER_DATA *provData = data->hWVTStateData;
329
330 TRACE("(%p, %s, %p)\n", hwnd, debugstr_guid(actionID), data);
331
332 if (provData)
333 {
334 if (provData->psPfns->pfnCleanupPolicy)
335 err = provData->psPfns->pfnCleanupPolicy(provData);
336
337 WINTRUST_Free(provData->padwTrustStepErrors);
338 WINTRUST_Free(provData->u.pPDSip);
339 WINTRUST_Free(provData->psPfns);
340 WINTRUST_Free(provData);
341 data->hWVTStateData = NULL;
342 }
343 TRACE("returning %08x\n", err);
344 return err;
345 }
346
347 static LONG WINTRUST_DefaultVerifyAndClose(HWND hwnd, GUID *actionID,
348 WINTRUST_DATA *data)
349 {
350 LONG err;
351
352 TRACE("(%p, %s, %p)\n", hwnd, debugstr_guid(actionID), data);
353
354 err = WINTRUST_DefaultVerify(hwnd, actionID, data);
355 WINTRUST_DefaultClose(hwnd, actionID, data);
356 TRACE("returning %08x\n", err);
357 return err;
358 }
359
360 static LONG WINTRUST_PublishedSoftware(HWND hwnd, GUID *actionID,
361 WINTRUST_DATA *data)
362 {
363 WINTRUST_DATA wintrust_data = { sizeof(wintrust_data), 0 };
364 /* Undocumented: the published software action is passed a path,
365 * and pSIPClientData points to a WIN_TRUST_SUBJECT_FILE.
366 */
367 LPWIN_TRUST_SUBJECT_FILE subjectFile = data->pSIPClientData;
368 WINTRUST_FILE_INFO fileInfo = { sizeof(fileInfo), 0 };
369
370 TRACE("subjectFile->hFile: %p\n", subjectFile->hFile);
371 TRACE("subjectFile->lpPath: %s\n", debugstr_w(subjectFile->lpPath));
372 fileInfo.pcwszFilePath = subjectFile->lpPath;
373 fileInfo.hFile = subjectFile->hFile;
374 wintrust_data.u.pFile = &fileInfo;
375 wintrust_data.dwUnionChoice = WTD_CHOICE_FILE;
376 wintrust_data.dwUIChoice = WTD_UI_NONE;
377
378 return WINTRUST_DefaultVerifyAndClose(hwnd, actionID, &wintrust_data);
379 }
380
381 /* Sadly, the function to load the cert for the CERT_CERTIFICATE_ACTION_VERIFY
382 * action is not stored in the registry and is located in wintrust, not in
383 * cryptdlg along with the rest of the implementation (verified by running the
384 * action with a native wintrust.dll.)
385 */
386 static HRESULT WINAPI WINTRUST_CertVerifyObjTrust(CRYPT_PROVIDER_DATA *data)
387 {
388 BOOL ret;
389
390 TRACE("(%p)\n", data);
391
392 if (!data->padwTrustStepErrors)
393 return S_FALSE;
394
395 switch (data->pWintrustData->dwUnionChoice)
396 {
397 case WTD_CHOICE_BLOB:
398 if (data->pWintrustData->u.pBlob &&
399 WVT_IS_CBSTRUCT_GT_MEMBEROFFSET(WINTRUST_BLOB_INFO,
400 data->pWintrustData->u.pBlob->cbStruct, pbMemObject) &&
401 data->pWintrustData->u.pBlob->cbMemObject ==
402 sizeof(CERT_VERIFY_CERTIFICATE_TRUST) &&
403 data->pWintrustData->u.pBlob->pbMemObject)
404 {
405 CERT_VERIFY_CERTIFICATE_TRUST *pCert =
406 (CERT_VERIFY_CERTIFICATE_TRUST *)
407 data->pWintrustData->u.pBlob->pbMemObject;
408
409 if (pCert->cbSize == sizeof(CERT_VERIFY_CERTIFICATE_TRUST) &&
410 pCert->pccert)
411 {
412 CRYPT_PROVIDER_SGNR signer = { sizeof(signer), { 0 } };
413 DWORD i;
414 SYSTEMTIME sysTime;
415
416 /* Add a signer with nothing but the time to verify, so we can
417 * add a cert to it
418 */
419 GetSystemTime(&sysTime);
420 SystemTimeToFileTime(&sysTime, &signer.sftVerifyAsOf);
421 ret = data->psPfns->pfnAddSgnr2Chain(data, FALSE, 0, &signer);
422 if (!ret)
423 goto error;
424 ret = data->psPfns->pfnAddCert2Chain(data, 0, FALSE, 0,
425 pCert->pccert);
426 if (!ret)
427 goto error;
428 for (i = 0; ret && i < pCert->cRootStores; i++)
429 ret = data->psPfns->pfnAddStore2Chain(data,
430 pCert->rghstoreRoots[i]);
431 for (i = 0; ret && i < pCert->cStores; i++)
432 ret = data->psPfns->pfnAddStore2Chain(data,
433 pCert->rghstoreCAs[i]);
434 for (i = 0; ret && i < pCert->cTrustStores; i++)
435 ret = data->psPfns->pfnAddStore2Chain(data,
436 pCert->rghstoreTrust[i]);
437 }
438 else
439 {
440 SetLastError(ERROR_INVALID_PARAMETER);
441 ret = FALSE;
442 }
443 }
444 else
445 {
446 SetLastError(ERROR_INVALID_PARAMETER);
447 ret = FALSE;
448 }
449 break;
450 default:
451 FIXME("unimplemented for %d\n", data->pWintrustData->dwUnionChoice);
452 SetLastError(ERROR_INVALID_PARAMETER);
453 ret = FALSE;
454 }
455
456 error:
457 if (!ret)
458 data->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] =
459 GetLastError();
460 TRACE("returning %d (%08x)\n", ret ? S_OK : S_FALSE,
461 data->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV]);
462 return ret ? S_OK : S_FALSE;
463 }
464
465 static LONG WINTRUST_CertVerify(HWND hwnd, GUID *actionID,
466 WINTRUST_DATA *data)
467 {
468 DWORD err = ERROR_SUCCESS, numSteps = 0;
469 CRYPT_PROVIDER_DATA *provData;
470 BOOL ret;
471 struct wintrust_step verifySteps[5];
472
473 TRACE("(%p, %s, %p)\n", hwnd, debugstr_guid(actionID), data);
474
475 provData = WINTRUST_AllocateProviderData();
476 if (!provData)
477 return ERROR_OUTOFMEMORY;
478
479 ret = WintrustLoadFunctionPointers(actionID, provData->psPfns);
480 if (!ret)
481 {
482 err = GetLastError();
483 goto error;
484 }
485 if (!provData->psPfns->pfnObjectTrust)
486 provData->psPfns->pfnObjectTrust = WINTRUST_CertVerifyObjTrust;
487 /* Not sure why, but native skips the policy check */
488 provData->psPfns->pfnCertCheckPolicy = NULL;
489
490 data->hWVTStateData = provData;
491 provData->pWintrustData = data;
492 if (hwnd == INVALID_HANDLE_VALUE)
493 provData->hWndParent = GetDesktopWindow();
494 else
495 provData->hWndParent = hwnd;
496 provData->pgActionID = actionID;
497 WintrustGetRegPolicyFlags(&provData->dwRegPolicySettings);
498
499 numSteps = WINTRUST_AddTrustStepsFromFunctions(verifySteps,
500 provData->psPfns);
501 err = WINTRUST_ExecuteSteps(verifySteps, numSteps, provData);
502 goto done;
503
504 error:
505 if (provData)
506 {
507 WINTRUST_Free(provData->padwTrustStepErrors);
508 WINTRUST_Free(provData->u.pPDSip);
509 WINTRUST_Free(provData->psPfns);
510 WINTRUST_Free(provData);
511 }
512 done:
513 TRACE("returning %08x\n", err);
514 return err;
515 }
516
517 static LONG WINTRUST_CertVerifyAndClose(HWND hwnd, GUID *actionID,
518 WINTRUST_DATA *data)
519 {
520 LONG err;
521
522 TRACE("(%p, %s, %p)\n", hwnd, debugstr_guid(actionID), data);
523
524 err = WINTRUST_CertVerify(hwnd, actionID, data);
525 WINTRUST_DefaultClose(hwnd, actionID, data);
526 TRACE("returning %08x\n", err);
527 return err;
528 }
529
530 static LONG WINTRUST_CertActionVerify(HWND hwnd, GUID *actionID,
531 WINTRUST_DATA *data)
532 {
533 DWORD stateAction;
534 LONG err = ERROR_SUCCESS;
535
536 if (WVT_ISINSTRUCT(WINTRUST_DATA, data->cbStruct, dwStateAction))
537 stateAction = data->dwStateAction;
538 else
539 {
540 TRACE("no dwStateAction, assuming WTD_STATEACTION_IGNORE\n");
541 stateAction = WTD_STATEACTION_IGNORE;
542 }
543 switch (stateAction)
544 {
545 case WTD_STATEACTION_IGNORE:
546 err = WINTRUST_CertVerifyAndClose(hwnd, actionID, data);
547 break;
548 case WTD_STATEACTION_VERIFY:
549 err = WINTRUST_CertVerify(hwnd, actionID, data);
550 break;
551 case WTD_STATEACTION_CLOSE:
552 err = WINTRUST_DefaultClose(hwnd, actionID, data);
553 break;
554 default:
555 FIXME("unimplemented for %d\n", data->dwStateAction);
556 }
557 return err;
558 }
559
560 static void dump_file_info(WINTRUST_FILE_INFO *pFile)
561 {
562 TRACE("%p\n", pFile);
563 if (pFile)
564 {
565 TRACE("cbStruct: %d\n", pFile->cbStruct);
566 TRACE("pcwszFilePath: %s\n", debugstr_w(pFile->pcwszFilePath));
567 TRACE("hFile: %p\n", pFile->hFile);
568 TRACE("pgKnownSubject: %s\n", debugstr_guid(pFile->pgKnownSubject));
569 }
570 }
571
572 static void dump_catalog_info(WINTRUST_CATALOG_INFO *catalog)
573 {
574 TRACE("%p\n", catalog);
575 if (catalog)
576 {
577 TRACE("cbStruct: %d\n", catalog->cbStruct);
578 TRACE("dwCatalogVersion: %d\n", catalog->dwCatalogVersion);
579 TRACE("pcwszCatalogFilePath: %s\n",
580 debugstr_w(catalog->pcwszCatalogFilePath));
581 TRACE("pcwszMemberTag: %s\n", debugstr_w(catalog->pcwszMemberTag));
582 TRACE("pcwszMemberFilePath: %s\n",
583 debugstr_w(catalog->pcwszMemberFilePath));
584 TRACE("hMemberFile: %p\n", catalog->hMemberFile);
585 TRACE("pbCalculatedFileHash: %p\n", catalog->pbCalculatedFileHash);
586 TRACE("cbCalculatedFileHash: %d\n", catalog->cbCalculatedFileHash);
587 TRACE("pcCatalogContext: %p\n", catalog->pcCatalogContext);
588 }
589 }
590
591 static void dump_blob_info(WINTRUST_BLOB_INFO *blob)
592 {
593 TRACE("%p\n", blob);
594 if (blob)
595 {
596 TRACE("cbStruct: %d\n", blob->cbStruct);
597 TRACE("gSubject: %s\n", debugstr_guid(&blob->gSubject));
598 TRACE("pcwszDisplayName: %s\n", debugstr_w(blob->pcwszDisplayName));
599 TRACE("cbMemObject: %d\n", blob->cbMemObject);
600 TRACE("pbMemObject: %p\n", blob->pbMemObject);
601 TRACE("cbMemSignedMsg: %d\n", blob->cbMemSignedMsg);
602 TRACE("pbMemSignedMsg: %p\n", blob->pbMemSignedMsg);
603 }
604 }
605
606 static void dump_sgnr_info(WINTRUST_SGNR_INFO *sgnr)
607 {
608 TRACE("%p\n", sgnr);
609 if (sgnr)
610 {
611 TRACE("cbStruct: %d\n", sgnr->cbStruct);
612 TRACE("pcwszDisplayName: %s\n", debugstr_w(sgnr->pcwszDisplayName));
613 TRACE("psSignerInfo: %p\n", sgnr->psSignerInfo);
614 TRACE("chStores: %d\n", sgnr->chStores);
615 }
616 }
617
618 static void dump_cert_info(WINTRUST_CERT_INFO *cert)
619 {
620 TRACE("%p\n", cert);
621 if (cert)
622 {
623 TRACE("cbStruct: %d\n", cert->cbStruct);
624 TRACE("pcwszDisplayName: %s\n", debugstr_w(cert->pcwszDisplayName));
625 TRACE("psCertContext: %p\n", cert->psCertContext);
626 TRACE("chStores: %d\n", cert->chStores);
627 TRACE("dwFlags: %08x\n", cert->dwFlags);
628 TRACE("psftVerifyAsOf: %p\n", cert->psftVerifyAsOf);
629 }
630 }
631
632 static void dump_wintrust_data(WINTRUST_DATA *data)
633 {
634 TRACE("%p\n", data);
635 if (data)
636 {
637 TRACE("cbStruct: %d\n", data->cbStruct);
638 TRACE("pPolicyCallbackData: %p\n", data->pPolicyCallbackData);
639 TRACE("pSIPClientData: %p\n", data->pSIPClientData);
640 TRACE("dwUIChoice: %d\n", data->dwUIChoice);
641 TRACE("fdwRevocationChecks: %08x\n", data->fdwRevocationChecks);
642 TRACE("dwUnionChoice: %d\n", data->dwUnionChoice);
643 switch (data->dwUnionChoice)
644 {
645 case WTD_CHOICE_FILE:
646 dump_file_info(data->u.pFile);
647 break;
648 case WTD_CHOICE_CATALOG:
649 dump_catalog_info(data->u.pCatalog);
650 break;
651 case WTD_CHOICE_BLOB:
652 dump_blob_info(data->u.pBlob);
653 break;
654 case WTD_CHOICE_SIGNER:
655 dump_sgnr_info(data->u.pSgnr);
656 break;
657 case WTD_CHOICE_CERT:
658 dump_cert_info(data->u.pCert);
659 break;
660 }
661 TRACE("dwStateAction: %d\n", data->dwStateAction);
662 TRACE("hWVTStateData: %p\n", data->hWVTStateData);
663 TRACE("pwszURLReference: %s\n", debugstr_w(data->pwszURLReference));
664 TRACE("dwProvFlags: %08x\n", data->dwProvFlags);
665 TRACE("dwUIContext: %d\n", data->dwUIContext);
666 }
667 }
668
669 /***********************************************************************
670 * WinVerifyTrust (WINTRUST.@)
671 *
672 * Verifies an object by calling the specified trust provider.
673 *
674 * PARAMS
675 * hwnd [I] Handle to a caller window.
676 * ActionID [I] Pointer to a GUID that identifies the action to perform.
677 * ActionData [I] Information used by the trust provider to verify the object.
678 *
679 * RETURNS
680 * Success: Zero.
681 * Failure: A TRUST_E_* error code.
682 *
683 * NOTES
684 * Trust providers can be found at:
685 * HKLM\SOFTWARE\Microsoft\Cryptography\Providers\Trust\
686 */
687 LONG WINAPI WinVerifyTrust( HWND hwnd, GUID *ActionID, LPVOID ActionData )
688 {
689 static const GUID unknown = { 0xC689AAB8, 0x8E78, 0x11D0, { 0x8C,0x47,
690 0x00,0xC0,0x4F,0xC2,0x95,0xEE } };
691 static const GUID published_software = WIN_SPUB_ACTION_PUBLISHED_SOFTWARE;
692 static const GUID generic_verify_v2 = WINTRUST_ACTION_GENERIC_VERIFY_V2;
693 static const GUID generic_cert_verify = WINTRUST_ACTION_GENERIC_CERT_VERIFY;
694 static const GUID generic_chain_verify = WINTRUST_ACTION_GENERIC_CHAIN_VERIFY;
695 static const GUID cert_action_verify = CERT_CERTIFICATE_ACTION_VERIFY;
696 LONG err = ERROR_SUCCESS;
697 WINTRUST_DATA *actionData = ActionData;
698
699 TRACE("(%p, %s, %p)\n", hwnd, debugstr_guid(ActionID), ActionData);
700 dump_wintrust_data(ActionData);
701
702 /* Support for known old-style callers: */
703 if (IsEqualGUID(ActionID, &published_software))
704 err = WINTRUST_PublishedSoftware(hwnd, ActionID, ActionData);
705 else if (IsEqualGUID(ActionID, &cert_action_verify))
706 err = WINTRUST_CertActionVerify(hwnd, ActionID, ActionData);
707 else
708 {
709 DWORD stateAction;
710
711 /* Check known actions to warn of possible problems */
712 if (!IsEqualGUID(ActionID, &unknown) &&
713 !IsEqualGUID(ActionID, &generic_verify_v2) &&
714 !IsEqualGUID(ActionID, &generic_cert_verify) &&
715 !IsEqualGUID(ActionID, &generic_chain_verify))
716 WARN("unknown action %s, default behavior may not be right\n",
717 debugstr_guid(ActionID));
718 if (WVT_ISINSTRUCT(WINTRUST_DATA, actionData->cbStruct, dwStateAction))
719 stateAction = actionData->dwStateAction;
720 else
721 {
722 TRACE("no dwStateAction, assuming WTD_STATEACTION_IGNORE\n");
723 stateAction = WTD_STATEACTION_IGNORE;
724 }
725 switch (stateAction)
726 {
727 case WTD_STATEACTION_IGNORE:
728 err = WINTRUST_DefaultVerifyAndClose(hwnd, ActionID, ActionData);
729 break;
730 case WTD_STATEACTION_VERIFY:
731 err = WINTRUST_DefaultVerify(hwnd, ActionID, ActionData);
732 break;
733 case WTD_STATEACTION_CLOSE:
734 err = WINTRUST_DefaultClose(hwnd, ActionID, ActionData);
735 break;
736 default:
737 FIXME("unimplemented for %d\n", actionData->dwStateAction);
738 }
739 }
740
741 TRACE("returning %08x\n", err);
742 return err;
743 }
744
745 /***********************************************************************
746 * WinVerifyTrustEx (WINTRUST.@)
747 */
748 HRESULT WINAPI WinVerifyTrustEx( HWND hwnd, GUID *ActionID,
749 WINTRUST_DATA* ActionData )
750 {
751 return WinVerifyTrust(hwnd, ActionID, ActionData);
752 }
753
754 /***********************************************************************
755 * WTHelperGetProvSignerFromChain (WINTRUST.@)
756 */
757 CRYPT_PROVIDER_SGNR * WINAPI WTHelperGetProvSignerFromChain(
758 CRYPT_PROVIDER_DATA *pProvData, DWORD idxSigner, BOOL fCounterSigner,
759 DWORD idxCounterSigner)
760 {
761 CRYPT_PROVIDER_SGNR *sgnr;
762
763 TRACE("(%p %d %d %d)\n", pProvData, idxSigner, fCounterSigner,
764 idxCounterSigner);
765
766 if (idxSigner >= pProvData->csSigners || !pProvData->pasSigners)
767 return NULL;
768 sgnr = &pProvData->pasSigners[idxSigner];
769 if (fCounterSigner)
770 {
771 if (idxCounterSigner >= sgnr->csCounterSigners ||
772 !sgnr->pasCounterSigners)
773 return NULL;
774 sgnr = &sgnr->pasCounterSigners[idxCounterSigner];
775 }
776 TRACE("returning %p\n", sgnr);
777 return sgnr;
778 }
779
780 /***********************************************************************
781 * WTHelperGetProvCertFromChain (WINTRUST.@)
782 */
783 CRYPT_PROVIDER_CERT * WINAPI WTHelperGetProvCertFromChain(
784 CRYPT_PROVIDER_SGNR *pSgnr, DWORD idxCert)
785 {
786 CRYPT_PROVIDER_CERT *cert;
787
788 TRACE("(%p %d)\n", pSgnr, idxCert);
789
790 if (!pSgnr || idxCert >= pSgnr->csCertChain || !pSgnr->pasCertChain)
791 return NULL;
792 cert = &pSgnr->pasCertChain[idxCert];
793 TRACE("returning %p\n", cert);
794 return cert;
795 }
796
797 CRYPT_PROVIDER_PRIVDATA *WINAPI WTHelperGetProvPrivateDataFromChain(
798 CRYPT_PROVIDER_DATA* pProvData,
799 GUID* pgProviderID)
800 {
801 CRYPT_PROVIDER_PRIVDATA *privdata = NULL;
802 DWORD i;
803
804 TRACE("(%p, %s)\n", pProvData, debugstr_guid(pgProviderID));
805
806 for (i = 0; i < pProvData->csProvPrivData; i++)
807 if (IsEqualGUID(pgProviderID, &pProvData->pasProvPrivData[i].gProviderID))
808 {
809 privdata = &pProvData->pasProvPrivData[i];
810 break;
811 }
812
813 return privdata;
814 }
815
816 /***********************************************************************
817 * WTHelperProvDataFromStateData (WINTRUST.@)
818 */
819 CRYPT_PROVIDER_DATA * WINAPI WTHelperProvDataFromStateData(HANDLE hStateData)
820 {
821 TRACE("%p\n", hStateData);
822 return hStateData;
823 }
824
825 /***********************************************************************
826 * WTHelperGetFileName(WINTRUST.@)
827 */
828 LPCWSTR WINAPI WTHelperGetFileName(WINTRUST_DATA *data)
829 {
830 TRACE("%p\n",data);
831 if (data->dwUnionChoice == WTD_CHOICE_FILE)
832 return data->u.pFile->pcwszFilePath;
833 else
834 return NULL;
835 }
836
837 /***********************************************************************
838 * WTHelperGetFileHandle(WINTRUST.@)
839 */
840 HANDLE WINAPI WTHelperGetFileHandle(WINTRUST_DATA *data)
841 {
842 TRACE("%p\n",data);
843 if (data->dwUnionChoice == WTD_CHOICE_FILE)
844 return data->u.pFile->hFile;
845 else
846 return INVALID_HANDLE_VALUE;
847 }
848
849 static BOOL WINAPI WINTRUST_enumUsages(PCCRYPT_OID_INFO pInfo, void *pvArg)
850 {
851 PCCRYPT_OID_INFO **usages = pvArg;
852 DWORD cUsages;
853 BOOL ret;
854
855 if (!*usages)
856 {
857 cUsages = 0;
858 *usages = WINTRUST_Alloc(2 * sizeof(PCCRYPT_OID_INFO));
859 }
860 else
861 {
862 PCCRYPT_OID_INFO *ptr;
863
864 /* Count the existing usages.
865 * FIXME: make sure the new usage doesn't duplicate any in the list?
866 */
867 for (cUsages = 0, ptr = *usages; *ptr; ptr++, cUsages++)
868 ;
869 *usages = WINTRUST_ReAlloc(*usages,
870 (cUsages + 2) * sizeof(PCCRYPT_OID_INFO));
871 }
872 if (*usages)
873 {
874 (*usages)[cUsages] = pInfo;
875 (*usages)[cUsages + 1] = NULL;
876 ret = TRUE;
877 }
878 else
879 {
880 SetLastError(ERROR_OUTOFMEMORY);
881 ret = FALSE;
882 }
883 return ret;
884 }
885
886 /***********************************************************************
887 * WTHelperGetKnownUsages(WINTRUST.@)
888 *
889 * Enumerates the known enhanced key usages as an array of PCCRYPT_OID_INFOs.
890 *
891 * PARAMS
892 * action [In] 1 => allocate and return known usages, 2 => free previously
893 * allocated usages.
894 * usages [In/Out] If action == 1, *usages is set to an array of
895 * PCCRYPT_OID_INFO *. The array is terminated with a NULL
896 * pointer.
897 * If action == 2, *usages is freed.
898 *
899 * RETURNS
900 * TRUE on success, FALSE on failure.
901 */
902 BOOL WINAPI WTHelperGetKnownUsages(DWORD action, PCCRYPT_OID_INFO **usages)
903 {
904 BOOL ret;
905
906 TRACE("(%d, %p)\n", action, usages);
907
908 if (!usages)
909 {
910 SetLastError(ERROR_INVALID_PARAMETER);
911 return FALSE;
912 }
913
914 if (action == 1)
915 {
916 *usages = NULL;
917 ret = CryptEnumOIDInfo(CRYPT_ENHKEY_USAGE_OID_GROUP_ID, 0, usages,
918 WINTRUST_enumUsages);
919 }
920 else if (action == 2)
921 {
922 WINTRUST_Free(*usages);
923 *usages = NULL;
924 ret = TRUE;
925 }
926 else
927 {
928 WARN("unknown action %d\n", action);
929 SetLastError(ERROR_INVALID_PARAMETER);
930 ret = FALSE;
931 }
932 return ret;
933 }
934
935 static const WCHAR Software_Publishing[] = {
936 'S','o','f','t','w','a','r','e','\\',
937 'M','i','c','r','o','s','o','f','t','\\',
938 'W','i','n','d','o','w','s','\\',
939 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
940 'W','i','n','t','r','u','s','t','\\',
941 'T','r','u','s','t',' ','P','r','o','v','i','d','e','r','s','\\',
942 'S','o','f','t','w','a','r','e',' ',
943 'P','u','b','l','i','s','h','i','n','g',0 };
944 static const WCHAR State[] = { 'S','t','a','t','e',0 };
945
946 /***********************************************************************
947 * WintrustGetRegPolicyFlags (WINTRUST.@)
948 */
949 void WINAPI WintrustGetRegPolicyFlags( DWORD* pdwPolicyFlags )
950 {
951 HKEY key;
952 LONG r;
953
954 TRACE("%p\n", pdwPolicyFlags);
955
956 *pdwPolicyFlags = 0;
957 r = RegCreateKeyExW(HKEY_CURRENT_USER, Software_Publishing, 0, NULL, 0,
958 KEY_READ, NULL, &key, NULL);
959 if (!r)
960 {
961 DWORD size = sizeof(DWORD);
962
963 r = RegQueryValueExW(key, State, NULL, NULL, (LPBYTE)pdwPolicyFlags,
964 &size);
965 RegCloseKey(key);
966 if (r)
967 {
968 /* Failed to query, create and return default value */
969 *pdwPolicyFlags = WTPF_IGNOREREVOCATIONONTS |
970 WTPF_OFFLINEOKNBU_COM |
971 WTPF_OFFLINEOKNBU_IND |
972 WTPF_OFFLINEOK_COM |
973 WTPF_OFFLINEOK_IND;
974 WintrustSetRegPolicyFlags(*pdwPolicyFlags);
975 }
976 }
977 }
978
979 /***********************************************************************
980 * WintrustSetRegPolicyFlags (WINTRUST.@)
981 */
982 BOOL WINAPI WintrustSetRegPolicyFlags( DWORD dwPolicyFlags)
983 {
984 HKEY key;
985 LONG r;
986
987 TRACE("%x\n", dwPolicyFlags);
988
989 r = RegCreateKeyExW(HKEY_CURRENT_USER, Software_Publishing, 0,
990 NULL, 0, KEY_WRITE, NULL, &key, NULL);
991 if (!r)
992 {
993 r = RegSetValueExW(key, State, 0, REG_DWORD, (LPBYTE)&dwPolicyFlags,
994 sizeof(DWORD));
995 RegCloseKey(key);
996 }
997 if (r) SetLastError(r);
998 return r == ERROR_SUCCESS;
999 }
1000
1001 /* Utility functions */
1002
1003 BOOL WINAPI WINTRUST_AddStore(CRYPT_PROVIDER_DATA *data, HCERTSTORE store)
1004 {
1005 BOOL ret = FALSE;
1006
1007 TRACE("(%p, %p)\n", data, store);
1008
1009 if (data->chStores)
1010 data->pahStores = WINTRUST_ReAlloc(data->pahStores,
1011 (data->chStores + 1) * sizeof(HCERTSTORE));
1012 else
1013 {
1014 data->pahStores = WINTRUST_Alloc(sizeof(HCERTSTORE));
1015 data->chStores = 0;
1016 }
1017 if (data->pahStores)
1018 {
1019 data->pahStores[data->chStores++] = CertDuplicateStore(store);
1020 ret = TRUE;
1021 }
1022 else
1023 SetLastError(ERROR_OUTOFMEMORY);
1024 return ret;
1025 }
1026
1027 BOOL WINAPI WINTRUST_AddSgnr(CRYPT_PROVIDER_DATA *data,
1028 BOOL fCounterSigner, DWORD idxSigner, CRYPT_PROVIDER_SGNR *sgnr)
1029 {
1030 BOOL ret = FALSE;
1031
1032 TRACE("(%p, %d, %d, %p)\n", data, fCounterSigner, idxSigner, sgnr);
1033
1034 if (sgnr->cbStruct > sizeof(CRYPT_PROVIDER_SGNR))
1035 {
1036 SetLastError(ERROR_INVALID_PARAMETER);
1037 return FALSE;
1038 }
1039 if (fCounterSigner)
1040 {
1041 FIXME("unimplemented for counter signers\n");
1042 SetLastError(ERROR_INVALID_PARAMETER);
1043 return FALSE;
1044 }
1045 if (data->csSigners)
1046 data->pasSigners = WINTRUST_ReAlloc(data->pasSigners,
1047 (data->csSigners + 1) * sizeof(CRYPT_PROVIDER_SGNR));
1048 else
1049 {
1050 data->pasSigners = WINTRUST_Alloc(sizeof(CRYPT_PROVIDER_SGNR));
1051 data->csSigners = 0;
1052 }
1053 if (data->pasSigners)
1054 {
1055 if (idxSigner < data->csSigners)
1056 memmove(&data->pasSigners[idxSigner],
1057 &data->pasSigners[idxSigner + 1],
1058 (data->csSigners - idxSigner) * sizeof(CRYPT_PROVIDER_SGNR));
1059 ret = TRUE;
1060 if (sgnr->cbStruct == sizeof(CRYPT_PROVIDER_SGNR))
1061 {
1062 /* The PSDK says psSigner should be allocated using pfnAlloc, but
1063 * it doesn't say anything about ownership. Since callers are
1064 * internal, assume ownership is passed, and just store the
1065 * pointer.
1066 */
1067 memcpy(&data->pasSigners[idxSigner], sgnr,
1068 sizeof(CRYPT_PROVIDER_SGNR));
1069 }
1070 else
1071 memset(&data->pasSigners[idxSigner], 0,
1072 sizeof(CRYPT_PROVIDER_SGNR));
1073 data->csSigners++;
1074 }
1075 else
1076 SetLastError(ERROR_OUTOFMEMORY);
1077 return ret;
1078 }
1079
1080 BOOL WINAPI WINTRUST_AddCert(CRYPT_PROVIDER_DATA *data, DWORD idxSigner,
1081 BOOL fCounterSigner, DWORD idxCounterSigner, PCCERT_CONTEXT pCert2Add)
1082 {
1083 BOOL ret = FALSE;
1084
1085 TRACE("(%p, %d, %d, %d, %p)\n", data, idxSigner, fCounterSigner,
1086 idxSigner, pCert2Add);
1087
1088 if (fCounterSigner)
1089 {
1090 FIXME("unimplemented for counter signers\n");
1091 SetLastError(ERROR_INVALID_PARAMETER);
1092 return FALSE;
1093 }
1094 if (data->pasSigners[idxSigner].csCertChain)
1095 data->pasSigners[idxSigner].pasCertChain =
1096 WINTRUST_ReAlloc(data->pasSigners[idxSigner].pasCertChain,
1097 (data->pasSigners[idxSigner].csCertChain + 1) *
1098 sizeof(CRYPT_PROVIDER_CERT));
1099 else
1100 {
1101 data->pasSigners[idxSigner].pasCertChain =
1102 WINTRUST_Alloc(sizeof(CRYPT_PROVIDER_CERT));
1103 data->pasSigners[idxSigner].csCertChain = 0;
1104 }
1105 if (data->pasSigners[idxSigner].pasCertChain)
1106 {
1107 CRYPT_PROVIDER_CERT *cert = &data->pasSigners[idxSigner].pasCertChain[
1108 data->pasSigners[idxSigner].csCertChain];
1109
1110 cert->cbStruct = sizeof(CRYPT_PROVIDER_CERT);
1111 cert->pCert = CertDuplicateCertificateContext(pCert2Add);
1112 data->pasSigners[idxSigner].csCertChain++;
1113 ret = TRUE;
1114 }
1115 else
1116 SetLastError(ERROR_OUTOFMEMORY);
1117 return ret;
1118 }
1119
1120 BOOL WINAPI WINTRUST_AddPrivData(CRYPT_PROVIDER_DATA *data,
1121 CRYPT_PROVIDER_PRIVDATA *pPrivData2Add)
1122 {
1123 BOOL ret = FALSE;
1124
1125 TRACE("(%p, %p)\n", data, pPrivData2Add);
1126
1127 if (pPrivData2Add->cbStruct > sizeof(CRYPT_PROVIDER_PRIVDATA))
1128 {
1129 SetLastError(ERROR_INVALID_PARAMETER);
1130 WARN("invalid struct size\n");
1131 return FALSE;
1132 }
1133 if (data->csProvPrivData)
1134 data->pasProvPrivData = WINTRUST_ReAlloc(data->pasProvPrivData,
1135 (data->csProvPrivData + 1) * sizeof(CRYPT_PROVIDER_SGNR));
1136 else
1137 {
1138 data->pasProvPrivData = WINTRUST_Alloc(sizeof(CRYPT_PROVIDER_SGNR));
1139 data->csProvPrivData = 0;
1140 }
1141 if (data->pasProvPrivData)
1142 {
1143 DWORD i;
1144
1145 for (i = 0; i < data->csProvPrivData; i++)
1146 if (IsEqualGUID(&pPrivData2Add->gProviderID, &data->pasProvPrivData[i]))
1147 break;
1148
1149 data->pasProvPrivData[i] = *pPrivData2Add;
1150 if (i == data->csProvPrivData)
1151 data->csProvPrivData++;
1152 }
1153 else
1154 SetLastError(ERROR_OUTOFMEMORY);
1155 return ret;
1156 }
1157
1158 /***********************************************************************
1159 * OpenPersonalTrustDBDialog (WINTRUST.@)
1160 *
1161 * Opens the certificate manager dialog, showing only the stores that
1162 * contain trusted software publishers.
1163 *
1164 * PARAMS
1165 * hwnd [I] handle of parent window
1166 *
1167 * RETURNS
1168 * TRUE if the dialog could be opened, FALSE if not.
1169 */
1170 BOOL WINAPI OpenPersonalTrustDBDialog(HWND hwnd)
1171 {
1172 CRYPTUI_CERT_MGR_STRUCT uiCertMgr;
1173
1174 uiCertMgr.dwSize = sizeof(uiCertMgr);
1175 uiCertMgr.hwndParent = hwnd;
1176 uiCertMgr.dwFlags = CRYPTUI_CERT_MGR_PUBLISHER_TAB;
1177 uiCertMgr.pwszTitle = NULL;
1178 uiCertMgr.pszInitUsageOID = NULL;
1179 return CryptUIDlgCertMgr(&uiCertMgr);
1180 }
1181
1182 /***********************************************************************
1183 * WTHelperCertCheckValidSignature
1184 */
1185 HRESULT WINAPI WTHelperCertCheckValidSignature(CRYPT_PROVIDER_DATA *pProvData)
1186 {
1187 FIXME("Stub\n");
1188 return S_OK;
1189 }
1190
1191 /***********************************************************************
1192 * IsCatalogFile
1193 */
1194 BOOL WINAPI IsCatalogFile(HANDLE hFile, WCHAR *pwszFileName)
1195 {
1196 static const GUID catGUID = { 0xDE351A43, 0x8E59, 0x11D0, { 0x8C,0x47,0x00,0xC0,0x4F,0xC2,0x95,0xEE }};
1197 GUID guid;
1198
1199 TRACE("(%p, %s)\n", hFile, debugstr_w(pwszFileName));
1200
1201 if (!CryptSIPRetrieveSubjectGuid(pwszFileName, hFile, &guid))
1202 return FALSE;
1203 return IsEqualGUID(&guid, &catGUID);
1204 }
1205
1206 /***********************************************************************
1207 * FindCertsByIssuer
1208 */
1209 HRESULT WINAPI FindCertsByIssuer(PCERT_CHAIN pCertChains, DWORD *pcbCertChains,
1210 DWORD *pcCertChains, BYTE* pbEncodedIssuerName, DWORD cbEncodedIssuerName,
1211 LPCWSTR pwszPurpose, DWORD dwKeySpec)
1212 {
1213 FIXME("(%p, %p, %p, %p, %d, %s, %d): stub\n", pCertChains, pcbCertChains,
1214 pcCertChains, pbEncodedIssuerName, cbEncodedIssuerName,
1215 debugstr_w(pwszPurpose), dwKeySpec);
1216 return E_FAIL;
1217 }