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