xref: /reactos/dll/win32/wintrust/wintrust_main.c (revision 8a978a17)
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 }
1212