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