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