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