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