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