| /* | |
| * Copyright 2001 Rein Klazes | |
| * Copyright 2007 Juan Lang | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include "config.h" | |
| #include <stdarg.h> | |
| #define NONAMELESSUNION | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winerror.h" | |
| #include "winreg.h" | |
| #include "guiddef.h" | |
| #include "wintrust.h" | |
| #include "softpub.h" | |
| #include "mscat.h" | |
| #include "objbase.h" | |
| #include "winuser.h" | |
| #include "cryptdlg.h" | |
| #include "cryptuiapi.h" | |
| #include "wintrust_priv.h" | |
| #include "wine/debug.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(wintrust); | |
| /* Utility functions */ | |
| void * WINAPI WINTRUST_Alloc(DWORD cb) | |
| { | |
| return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cb); | |
| } | |
| static void* WINTRUST_ReAlloc(void *ptr, DWORD cb) __WINE_ALLOC_SIZE(2); | |
| static void* WINTRUST_ReAlloc(void *ptr, DWORD cb) | |
| { | |
| return HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptr, cb); | |
| } | |
| void WINAPI WINTRUST_Free(void *p) | |
| { | |
| HeapFree(GetProcessHeap(), 0, p); | |
| } | |
| /*********************************************************************** | |
| * DllMain (WINTRUST.@) | |
| */ | |
| BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserved ) | |
| { | |
| switch(reason) | |
| { | |
| case DLL_PROCESS_ATTACH: | |
| DisableThreadLibraryCalls( inst ); | |
| break; | |
| } | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * TrustIsCertificateSelfSigned (WINTRUST.@) | |
| */ | |
| BOOL WINAPI TrustIsCertificateSelfSigned( PCCERT_CONTEXT cert ) | |
| { | |
| PCERT_EXTENSION ext; | |
| DWORD size; | |
| BOOL ret; | |
| TRACE("%p\n", cert); | |
| if ((ext = CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER2, | |
| cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension))) | |
| { | |
| CERT_AUTHORITY_KEY_ID2_INFO *info; | |
| ret = CryptDecodeObjectEx(cert->dwCertEncodingType, | |
| X509_AUTHORITY_KEY_ID2, ext->Value.pbData, ext->Value.cbData, | |
| CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, | |
| &info, &size); | |
| if (ret) | |
| { | |
| if (info->AuthorityCertIssuer.cAltEntry && | |
| info->AuthorityCertSerialNumber.cbData) | |
| { | |
| PCERT_ALT_NAME_ENTRY directoryName = NULL; | |
| DWORD i; | |
| for (i = 0; !directoryName && | |
| i < info->AuthorityCertIssuer.cAltEntry; i++) | |
| if (info->AuthorityCertIssuer.rgAltEntry[i].dwAltNameChoice | |
| == CERT_ALT_NAME_DIRECTORY_NAME) | |
| directoryName = | |
| &info->AuthorityCertIssuer.rgAltEntry[i]; | |
| if (directoryName) | |
| { | |
| ret = CertCompareCertificateName(cert->dwCertEncodingType, | |
| &directoryName->u.DirectoryName, &cert->pCertInfo->Issuer) | |
| && CertCompareIntegerBlob(&info->AuthorityCertSerialNumber, | |
| &cert->pCertInfo->SerialNumber); | |
| } | |
| else | |
| { | |
| FIXME("no supported name type in authority key id2\n"); | |
| ret = FALSE; | |
| } | |
| } | |
| else if (info->KeyId.cbData) | |
| { | |
| ret = CertGetCertificateContextProperty(cert, | |
| CERT_KEY_IDENTIFIER_PROP_ID, NULL, &size); | |
| if (ret && size == info->KeyId.cbData) | |
| { | |
| LPBYTE buf = CryptMemAlloc(size); | |
| if (buf) | |
| { | |
| CertGetCertificateContextProperty(cert, | |
| CERT_KEY_IDENTIFIER_PROP_ID, buf, &size); | |
| ret = !memcmp(buf, info->KeyId.pbData, size); | |
| CryptMemFree(buf); | |
| } | |
| else | |
| ret = FALSE; | |
| } | |
| else | |
| ret = FALSE; | |
| } | |
| LocalFree(info); | |
| } | |
| } | |
| else if ((ext = CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER, | |
| cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension))) | |
| { | |
| CERT_AUTHORITY_KEY_ID_INFO *info; | |
| ret = CryptDecodeObjectEx(cert->dwCertEncodingType, | |
| X509_AUTHORITY_KEY_ID, ext->Value.pbData, ext->Value.cbData, | |
| CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, | |
| &info, &size); | |
| if (ret) | |
| { | |
| if (info->CertIssuer.cbData && info->CertSerialNumber.cbData) | |
| { | |
| ret = CertCompareCertificateName(cert->dwCertEncodingType, | |
| &info->CertIssuer, &cert->pCertInfo->Issuer) && | |
| CertCompareIntegerBlob(&info->CertSerialNumber, | |
| &cert->pCertInfo->SerialNumber); | |
| } | |
| else if (info->KeyId.cbData) | |
| { | |
| ret = CertGetCertificateContextProperty(cert, | |
| CERT_KEY_IDENTIFIER_PROP_ID, NULL, &size); | |
| if (ret && size == info->KeyId.cbData) | |
| { | |
| LPBYTE buf = CryptMemAlloc(size); | |
| if (buf) | |
| { | |
| CertGetCertificateContextProperty(cert, | |
| CERT_KEY_IDENTIFIER_PROP_ID, buf, &size); | |
| ret = !memcmp(buf, info->KeyId.pbData, size); | |
| CryptMemFree(buf); | |
| } | |
| else | |
| ret = FALSE; | |
| } | |
| else | |
| ret = FALSE; | |
| } | |
| else | |
| ret = FALSE; | |
| LocalFree(info); | |
| } | |
| } | |
| else | |
| ret = CertCompareCertificateName(cert->dwCertEncodingType, | |
| &cert->pCertInfo->Subject, &cert->pCertInfo->Issuer); | |
| return ret; | |
| } | |
| typedef HRESULT (WINAPI *wintrust_step_func)(CRYPT_PROVIDER_DATA *data); | |
| struct wintrust_step | |
| { | |
| wintrust_step_func func; | |
| DWORD error_index; | |
| }; | |
| static DWORD WINTRUST_ExecuteSteps(const struct wintrust_step *steps, | |
| DWORD numSteps, CRYPT_PROVIDER_DATA *provData) | |
| { | |
| DWORD i, err = ERROR_SUCCESS; | |
| for (i = 0; !err && i < numSteps; i++) | |
| { | |
| err = steps[i].func(provData); | |
| if (err) | |
| err = provData->padwTrustStepErrors[steps[i].error_index]; | |
| } | |
| return err; | |
| } | |
| static CRYPT_PROVIDER_DATA *WINTRUST_AllocateProviderData(void) | |
| { | |
| CRYPT_PROVIDER_DATA *provData; | |
| provData = WINTRUST_Alloc(sizeof(CRYPT_PROVIDER_DATA)); | |
| if (!provData) | |
| goto oom; | |
| provData->cbStruct = sizeof(CRYPT_PROVIDER_DATA); | |
| provData->padwTrustStepErrors = | |
| WINTRUST_Alloc(TRUSTERROR_MAX_STEPS * sizeof(DWORD)); | |
| if (!provData->padwTrustStepErrors) | |
| goto oom; | |
| provData->cdwTrustStepErrors = TRUSTERROR_MAX_STEPS; | |
| provData->u.pPDSip = WINTRUST_Alloc(sizeof(PROVDATA_SIP)); | |
| if (!provData->u.pPDSip) | |
| goto oom; | |
| provData->u.pPDSip->cbStruct = sizeof(PROVDATA_SIP); | |
| provData->psPfns = WINTRUST_Alloc(sizeof(CRYPT_PROVIDER_FUNCTIONS)); | |
| if (!provData->psPfns) | |
| goto oom; | |
| provData->psPfns->cbStruct = sizeof(CRYPT_PROVIDER_FUNCTIONS); | |
| return provData; | |
| oom: | |
| if (provData) | |
| { | |
| WINTRUST_Free(provData->padwTrustStepErrors); | |
| WINTRUST_Free(provData->u.pPDSip); | |
| WINTRUST_Free(provData->psPfns); | |
| WINTRUST_Free(provData); | |
| } | |
| return NULL; | |
| } | |
| /* Adds trust steps for each function in psPfns. Assumes steps has at least | |
| * 5 entries. Returns the number of steps added. | |
| */ | |
| static DWORD WINTRUST_AddTrustStepsFromFunctions(struct wintrust_step *steps, | |
| const CRYPT_PROVIDER_FUNCTIONS *psPfns) | |
| { | |
| DWORD numSteps = 0; | |
| if (psPfns->pfnInitialize) | |
| { | |
| steps[numSteps].func = psPfns->pfnInitialize; | |
| steps[numSteps++].error_index = TRUSTERROR_STEP_FINAL_WVTINIT; | |
| } | |
| if (psPfns->pfnObjectTrust) | |
| { | |
| steps[numSteps].func = psPfns->pfnObjectTrust; | |
| steps[numSteps++].error_index = TRUSTERROR_STEP_FINAL_OBJPROV; | |
| } | |
| if (psPfns->pfnSignatureTrust) | |
| { | |
| steps[numSteps].func = psPfns->pfnSignatureTrust; | |
| steps[numSteps++].error_index = TRUSTERROR_STEP_FINAL_SIGPROV; | |
| } | |
| if (psPfns->pfnCertificateTrust) | |
| { | |
| steps[numSteps].func = psPfns->pfnCertificateTrust; | |
| steps[numSteps++].error_index = TRUSTERROR_STEP_FINAL_CERTPROV; | |
| } | |
| if (psPfns->pfnFinalPolicy) | |
| { | |
| steps[numSteps].func = psPfns->pfnFinalPolicy; | |
| steps[numSteps++].error_index = TRUSTERROR_STEP_FINAL_POLICYPROV; | |
| } | |
| return numSteps; | |
| } | |
| static LONG WINTRUST_DefaultVerify(HWND hwnd, GUID *actionID, | |
| WINTRUST_DATA *data) | |
| { | |
| DWORD err = ERROR_SUCCESS, numSteps = 0; | |
| CRYPT_PROVIDER_DATA *provData; | |
| BOOL ret; | |
| struct wintrust_step verifySteps[5]; | |
| TRACE("(%p, %s, %p)\n", hwnd, debugstr_guid(actionID), data); | |
| provData = WINTRUST_AllocateProviderData(); | |
| if (!provData) | |
| return ERROR_OUTOFMEMORY; | |
| ret = WintrustLoadFunctionPointers(actionID, provData->psPfns); | |
| if (!ret) | |
| { | |
| err = GetLastError(); | |
| goto error; | |
| } | |
| data->hWVTStateData = provData; | |
| provData->pWintrustData = data; | |
| if (hwnd == INVALID_HANDLE_VALUE) | |
| provData->hWndParent = GetDesktopWindow(); | |
| else | |
| provData->hWndParent = hwnd; | |
| provData->pgActionID = actionID; | |
| WintrustGetRegPolicyFlags(&provData->dwRegPolicySettings); | |
| numSteps = WINTRUST_AddTrustStepsFromFunctions(verifySteps, | |
| provData->psPfns); | |
| err = WINTRUST_ExecuteSteps(verifySteps, numSteps, provData); | |
| goto done; | |
| error: | |
| if (provData) | |
| { | |
| WINTRUST_Free(provData->padwTrustStepErrors); | |
| WINTRUST_Free(provData->u.pPDSip); | |
| WINTRUST_Free(provData->psPfns); | |
| WINTRUST_Free(provData); | |
| } | |
| done: | |
| TRACE("returning %08x\n", err); | |
| return err; | |
| } | |
| static LONG WINTRUST_DefaultClose(HWND hwnd, GUID *actionID, | |
| WINTRUST_DATA *data) | |
| { | |
| DWORD err = ERROR_SUCCESS; | |
| CRYPT_PROVIDER_DATA *provData = data->hWVTStateData; | |
| TRACE("(%p, %s, %p)\n", hwnd, debugstr_guid(actionID), data); | |
| if (provData) | |
| { | |
| if (provData->psPfns->pfnCleanupPolicy) | |
| err = provData->psPfns->pfnCleanupPolicy(provData); | |
| WINTRUST_Free(provData->padwTrustStepErrors); | |
| WINTRUST_Free(provData->u.pPDSip); | |
| WINTRUST_Free(provData->psPfns); | |
| WINTRUST_Free(provData); | |
| data->hWVTStateData = NULL; | |
| } | |
| TRACE("returning %08x\n", err); | |
| return err; | |
| } | |
| static LONG WINTRUST_DefaultVerifyAndClose(HWND hwnd, GUID *actionID, | |
| WINTRUST_DATA *data) | |
| { | |
| LONG err; | |
| TRACE("(%p, %s, %p)\n", hwnd, debugstr_guid(actionID), data); | |
| err = WINTRUST_DefaultVerify(hwnd, actionID, data); | |
| WINTRUST_DefaultClose(hwnd, actionID, data); | |
| TRACE("returning %08x\n", err); | |
| return err; | |
| } | |
| static LONG WINTRUST_PublishedSoftware(HWND hwnd, GUID *actionID, | |
| WINTRUST_DATA *data) | |
| { | |
| WINTRUST_DATA wintrust_data = { sizeof(wintrust_data), 0 }; | |
| /* Undocumented: the published software action is passed a path, | |
| * and pSIPClientData points to a WIN_TRUST_SUBJECT_FILE. | |
| */ | |
| LPWIN_TRUST_SUBJECT_FILE subjectFile = data->pSIPClientData; | |
| WINTRUST_FILE_INFO fileInfo = { sizeof(fileInfo), 0 }; | |
| TRACE("subjectFile->hFile: %p\n", subjectFile->hFile); | |
| TRACE("subjectFile->lpPath: %s\n", debugstr_w(subjectFile->lpPath)); | |
| fileInfo.pcwszFilePath = subjectFile->lpPath; | |
| fileInfo.hFile = subjectFile->hFile; | |
| wintrust_data.u.pFile = &fileInfo; | |
| wintrust_data.dwUnionChoice = WTD_CHOICE_FILE; | |
| wintrust_data.dwUIChoice = WTD_UI_NONE; | |
| return WINTRUST_DefaultVerifyAndClose(hwnd, actionID, &wintrust_data); | |
| } | |
| /* Sadly, the function to load the cert for the CERT_CERTIFICATE_ACTION_VERIFY | |
| * action is not stored in the registry and is located in wintrust, not in | |
| * cryptdlg along with the rest of the implementation (verified by running the | |
| * action with a native wintrust.dll.) | |
| */ | |
| static HRESULT WINAPI WINTRUST_CertVerifyObjTrust(CRYPT_PROVIDER_DATA *data) | |
| { | |
| BOOL ret; | |
| TRACE("(%p)\n", data); | |
| if (!data->padwTrustStepErrors) | |
| return S_FALSE; | |
| switch (data->pWintrustData->dwUnionChoice) | |
| { | |
| case WTD_CHOICE_BLOB: | |
| if (data->pWintrustData->u.pBlob && | |
| WVT_IS_CBSTRUCT_GT_MEMBEROFFSET(WINTRUST_BLOB_INFO, | |
| data->pWintrustData->u.pBlob->cbStruct, pbMemObject) && | |
| data->pWintrustData->u.pBlob->cbMemObject == | |
| sizeof(CERT_VERIFY_CERTIFICATE_TRUST) && | |
| data->pWintrustData->u.pBlob->pbMemObject) | |
| { | |
| CERT_VERIFY_CERTIFICATE_TRUST *pCert = | |
| (CERT_VERIFY_CERTIFICATE_TRUST *) | |
| data->pWintrustData->u.pBlob->pbMemObject; | |
| if (pCert->cbSize == sizeof(CERT_VERIFY_CERTIFICATE_TRUST) && | |
| pCert->pccert) | |
| { | |
| CRYPT_PROVIDER_SGNR signer = { sizeof(signer), { 0 } }; | |
| DWORD i; | |
| SYSTEMTIME sysTime; | |
| /* Add a signer with nothing but the time to verify, so we can | |
| * add a cert to it | |
| */ | |
| GetSystemTime(&sysTime); | |
| SystemTimeToFileTime(&sysTime, &signer.sftVerifyAsOf); | |
| ret = data->psPfns->pfnAddSgnr2Chain(data, FALSE, 0, &signer); | |
| if (!ret) | |
| goto error; | |
| ret = data->psPfns->pfnAddCert2Chain(data, 0, FALSE, 0, | |
| pCert->pccert); | |
| if (!ret) | |
| goto error; | |
| for (i = 0; ret && i < pCert->cRootStores; i++) | |
| ret = data->psPfns->pfnAddStore2Chain(data, | |
| pCert->rghstoreRoots[i]); | |
| for (i = 0; ret && i < pCert->cStores; i++) | |
| ret = data->psPfns->pfnAddStore2Chain(data, | |
| pCert->rghstoreCAs[i]); | |
| for (i = 0; ret && i < pCert->cTrustStores; i++) | |
| ret = data->psPfns->pfnAddStore2Chain(data, | |
| pCert->rghstoreTrust[i]); | |
| } | |
| else | |
| { | |
| SetLastError(ERROR_INVALID_PARAMETER); | |
| ret = FALSE; | |
| } | |
| } | |
| else | |
| { | |
| SetLastError(ERROR_INVALID_PARAMETER); | |
| ret = FALSE; | |
| } | |
| break; | |
| default: | |
| FIXME("unimplemented for %d\n", data->pWintrustData->dwUnionChoice); | |
| SetLastError(ERROR_INVALID_PARAMETER); | |
| ret = FALSE; | |
| } | |
| error: | |
| if (!ret) | |
| data->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = | |
| GetLastError(); | |
| TRACE("returning %d (%08x)\n", ret ? S_OK : S_FALSE, | |
| data->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV]); | |
| return ret ? S_OK : S_FALSE; | |
| } | |
| static LONG WINTRUST_CertVerify(HWND hwnd, GUID *actionID, | |
| WINTRUST_DATA *data) | |
| { | |
| DWORD err = ERROR_SUCCESS, numSteps = 0; | |
| CRYPT_PROVIDER_DATA *provData; | |
| BOOL ret; | |
| struct wintrust_step verifySteps[5]; | |
| TRACE("(%p, %s, %p)\n", hwnd, debugstr_guid(actionID), data); | |
| provData = WINTRUST_AllocateProviderData(); | |
| if (!provData) | |
| return ERROR_OUTOFMEMORY; | |
| ret = WintrustLoadFunctionPointers(actionID, provData->psPfns); | |
| if (!ret) | |
| { | |
| err = GetLastError(); | |
| goto error; | |
| } | |
| if (!provData->psPfns->pfnObjectTrust) | |
| provData->psPfns->pfnObjectTrust = WINTRUST_CertVerifyObjTrust; | |
| /* Not sure why, but native skips the policy check */ | |
| provData->psPfns->pfnCertCheckPolicy = NULL; | |
| data->hWVTStateData = provData; | |
| provData->pWintrustData = data; | |
| if (hwnd == INVALID_HANDLE_VALUE) | |
| provData->hWndParent = GetDesktopWindow(); | |
| else | |
| provData->hWndParent = hwnd; | |
| provData->pgActionID = actionID; | |
| WintrustGetRegPolicyFlags(&provData->dwRegPolicySettings); | |
| numSteps = WINTRUST_AddTrustStepsFromFunctions(verifySteps, | |
| provData->psPfns); | |
| err = WINTRUST_ExecuteSteps(verifySteps, numSteps, provData); | |
| goto done; | |
| error: | |
| if (provData) | |
| { | |
| WINTRUST_Free(provData->padwTrustStepErrors); | |
| WINTRUST_Free(provData->u.pPDSip); | |
| WINTRUST_Free(provData->psPfns); | |
| WINTRUST_Free(provData); | |
| } | |
| done: | |
| TRACE("returning %08x\n", err); | |
| return err; | |
| } | |
| static LONG WINTRUST_CertVerifyAndClose(HWND hwnd, GUID *actionID, | |
| WINTRUST_DATA *data) | |
| { | |
| LONG err; | |
| TRACE("(%p, %s, %p)\n", hwnd, debugstr_guid(actionID), data); | |
| err = WINTRUST_CertVerify(hwnd, actionID, data); | |
| WINTRUST_DefaultClose(hwnd, actionID, data); | |
| TRACE("returning %08x\n", err); | |
| return err; | |
| } | |
| static LONG WINTRUST_CertActionVerify(HWND hwnd, GUID *actionID, | |
| WINTRUST_DATA *data) | |
| { | |
| DWORD stateAction; | |
| LONG err = ERROR_SUCCESS; | |
| if (WVT_ISINSTRUCT(WINTRUST_DATA, data->cbStruct, dwStateAction)) | |
| stateAction = data->dwStateAction; | |
| else | |
| { | |
| TRACE("no dwStateAction, assuming WTD_STATEACTION_IGNORE\n"); | |
| stateAction = WTD_STATEACTION_IGNORE; | |
| } | |
| switch (stateAction) | |
| { | |
| case WTD_STATEACTION_IGNORE: | |
| err = WINTRUST_CertVerifyAndClose(hwnd, actionID, data); | |
| break; | |
| case WTD_STATEACTION_VERIFY: | |
| err = WINTRUST_CertVerify(hwnd, actionID, data); | |
| break; | |
| case WTD_STATEACTION_CLOSE: | |
| err = WINTRUST_DefaultClose(hwnd, actionID, data); | |
| break; | |
| default: | |
| FIXME("unimplemented for %d\n", data->dwStateAction); | |
| } | |
| return err; | |
| } | |
| static void dump_file_info(WINTRUST_FILE_INFO *pFile) | |
| { | |
| TRACE("%p\n", pFile); | |
| if (pFile) | |
| { | |
| TRACE("cbStruct: %d\n", pFile->cbStruct); | |
| TRACE("pcwszFilePath: %s\n", debugstr_w(pFile->pcwszFilePath)); | |
| TRACE("hFile: %p\n", pFile->hFile); | |
| TRACE("pgKnownSubject: %s\n", debugstr_guid(pFile->pgKnownSubject)); | |
| } | |
| } | |
| static void dump_catalog_info(WINTRUST_CATALOG_INFO *catalog) | |
| { | |
| TRACE("%p\n", catalog); | |
| if (catalog) | |
| { | |
| TRACE("cbStruct: %d\n", catalog->cbStruct); | |
| TRACE("dwCatalogVersion: %d\n", catalog->dwCatalogVersion); | |
| TRACE("pcwszCatalogFilePath: %s\n", | |
| debugstr_w(catalog->pcwszCatalogFilePath)); | |
| TRACE("pcwszMemberTag: %s\n", debugstr_w(catalog->pcwszMemberTag)); | |
| TRACE("pcwszMemberFilePath: %s\n", | |
| debugstr_w(catalog->pcwszMemberFilePath)); | |
| TRACE("hMemberFile: %p\n", catalog->hMemberFile); | |
| TRACE("pbCalculatedFileHash: %p\n", catalog->pbCalculatedFileHash); | |
| TRACE("cbCalculatedFileHash: %d\n", catalog->cbCalculatedFileHash); | |
| TRACE("pcCatalogContext: %p\n", catalog->pcCatalogContext); | |
| } | |
| } | |
| static void dump_blob_info(WINTRUST_BLOB_INFO *blob) | |
| { | |
| TRACE("%p\n", blob); | |
| if (blob) | |
| { | |
| TRACE("cbStruct: %d\n", blob->cbStruct); | |
| TRACE("gSubject: %s\n", debugstr_guid(&blob->gSubject)); | |
| TRACE("pcwszDisplayName: %s\n", debugstr_w(blob->pcwszDisplayName)); | |
| TRACE("cbMemObject: %d\n", blob->cbMemObject); | |
| TRACE("pbMemObject: %p\n", blob->pbMemObject); | |
| TRACE("cbMemSignedMsg: %d\n", blob->cbMemSignedMsg); | |
| TRACE("pbMemSignedMsg: %p\n", blob->pbMemSignedMsg); | |
| } | |
| } | |
| static void dump_sgnr_info(WINTRUST_SGNR_INFO *sgnr) | |
| { | |
| TRACE("%p\n", sgnr); | |
| if (sgnr) | |
| { | |
| TRACE("cbStruct: %d\n", sgnr->cbStruct); | |
| TRACE("pcwszDisplayName: %s\n", debugstr_w(sgnr->pcwszDisplayName)); | |
| TRACE("psSignerInfo: %p\n", sgnr->psSignerInfo); | |
| TRACE("chStores: %d\n", sgnr->chStores); | |
| } | |
| } | |
| static void dump_cert_info(WINTRUST_CERT_INFO *cert) | |
| { | |
| TRACE("%p\n", cert); | |
| if (cert) | |
| { | |
| TRACE("cbStruct: %d\n", cert->cbStruct); | |
| TRACE("pcwszDisplayName: %s\n", debugstr_w(cert->pcwszDisplayName)); | |
| TRACE("psCertContext: %p\n", cert->psCertContext); | |
| TRACE("chStores: %d\n", cert->chStores); | |
| TRACE("dwFlags: %08x\n", cert->dwFlags); | |
| TRACE("psftVerifyAsOf: %p\n", cert->psftVerifyAsOf); | |
| } | |
| } | |
| static void dump_wintrust_data(WINTRUST_DATA *data) | |
| { | |
| TRACE("%p\n", data); | |
| if (data) | |
| { | |
| TRACE("cbStruct: %d\n", data->cbStruct); | |
| TRACE("pPolicyCallbackData: %p\n", data->pPolicyCallbackData); | |
| TRACE("pSIPClientData: %p\n", data->pSIPClientData); | |
| TRACE("dwUIChoice: %d\n", data->dwUIChoice); | |
| TRACE("fdwRevocationChecks: %08x\n", data->fdwRevocationChecks); | |
| TRACE("dwUnionChoice: %d\n", data->dwUnionChoice); | |
| switch (data->dwUnionChoice) | |
| { | |
| case WTD_CHOICE_FILE: | |
| dump_file_info(data->u.pFile); | |
| break; | |
| case WTD_CHOICE_CATALOG: | |
| dump_catalog_info(data->u.pCatalog); | |
| break; | |
| case WTD_CHOICE_BLOB: | |
| dump_blob_info(data->u.pBlob); | |
| break; | |
| case WTD_CHOICE_SIGNER: | |
| dump_sgnr_info(data->u.pSgnr); | |
| break; | |
| case WTD_CHOICE_CERT: | |
| dump_cert_info(data->u.pCert); | |
| break; | |
| } | |
| TRACE("dwStateAction: %d\n", data->dwStateAction); | |
| TRACE("hWVTStateData: %p\n", data->hWVTStateData); | |
| TRACE("pwszURLReference: %s\n", debugstr_w(data->pwszURLReference)); | |
| TRACE("dwProvFlags: %08x\n", data->dwProvFlags); | |
| TRACE("dwUIContext: %d\n", data->dwUIContext); | |
| } | |
| } | |
| /*********************************************************************** | |
| * WinVerifyTrust (WINTRUST.@) | |
| * | |
| * Verifies an object by calling the specified trust provider. | |
| * | |
| * PARAMS | |
| * hwnd [I] Handle to a caller window. | |
| * ActionID [I] Pointer to a GUID that identifies the action to perform. | |
| * ActionData [I] Information used by the trust provider to verify the object. | |
| * | |
| * RETURNS | |
| * Success: Zero. | |
| * Failure: A TRUST_E_* error code. | |
| * | |
| * NOTES | |
| * Trust providers can be found at: | |
| * HKLM\SOFTWARE\Microsoft\Cryptography\Providers\Trust\ | |
| */ | |
| LONG WINAPI WinVerifyTrust( HWND hwnd, GUID *ActionID, LPVOID ActionData ) | |
| { | |
| static const GUID unknown = { 0xC689AAB8, 0x8E78, 0x11D0, { 0x8C,0x47, | |
| 0x00,0xC0,0x4F,0xC2,0x95,0xEE } }; | |
| static const GUID published_software = WIN_SPUB_ACTION_PUBLISHED_SOFTWARE; | |
| static const GUID generic_verify_v2 = WINTRUST_ACTION_GENERIC_VERIFY_V2; | |
| static const GUID generic_cert_verify = WINTRUST_ACTION_GENERIC_CERT_VERIFY; | |
| static const GUID generic_chain_verify = WINTRUST_ACTION_GENERIC_CHAIN_VERIFY; | |
| static const GUID cert_action_verify = CERT_CERTIFICATE_ACTION_VERIFY; | |
| LONG err = ERROR_SUCCESS; | |
| WINTRUST_DATA *actionData = ActionData; | |
| TRACE("(%p, %s, %p)\n", hwnd, debugstr_guid(ActionID), ActionData); | |
| dump_wintrust_data(ActionData); | |
| /* Support for known old-style callers: */ | |
| if (IsEqualGUID(ActionID, &published_software)) | |
| err = WINTRUST_PublishedSoftware(hwnd, ActionID, ActionData); | |
| else if (IsEqualGUID(ActionID, &cert_action_verify)) | |
| err = WINTRUST_CertActionVerify(hwnd, ActionID, ActionData); | |
| else | |
| { | |
| DWORD stateAction; | |
| /* Check known actions to warn of possible problems */ | |
| if (!IsEqualGUID(ActionID, &unknown) && | |
| !IsEqualGUID(ActionID, &generic_verify_v2) && | |
| !IsEqualGUID(ActionID, &generic_cert_verify) && | |
| !IsEqualGUID(ActionID, &generic_chain_verify)) | |
| WARN("unknown action %s, default behavior may not be right\n", | |
| debugstr_guid(ActionID)); | |
| if (WVT_ISINSTRUCT(WINTRUST_DATA, actionData->cbStruct, dwStateAction)) | |
| stateAction = actionData->dwStateAction; | |
| else | |
| { | |
| TRACE("no dwStateAction, assuming WTD_STATEACTION_IGNORE\n"); | |
| stateAction = WTD_STATEACTION_IGNORE; | |
| } | |
| switch (stateAction) | |
| { | |
| case WTD_STATEACTION_IGNORE: | |
| err = WINTRUST_DefaultVerifyAndClose(hwnd, ActionID, ActionData); | |
| break; | |
| case WTD_STATEACTION_VERIFY: | |
| err = WINTRUST_DefaultVerify(hwnd, ActionID, ActionData); | |
| break; | |
| case WTD_STATEACTION_CLOSE: | |
| err = WINTRUST_DefaultClose(hwnd, ActionID, ActionData); | |
| break; | |
| default: | |
| FIXME("unimplemented for %d\n", actionData->dwStateAction); | |
| } | |
| } | |
| TRACE("returning %08x\n", err); | |
| return err; | |
| } | |
| /*********************************************************************** | |
| * WinVerifyTrustEx (WINTRUST.@) | |
| */ | |
| HRESULT WINAPI WinVerifyTrustEx( HWND hwnd, GUID *ActionID, | |
| WINTRUST_DATA* ActionData ) | |
| { | |
| return WinVerifyTrust(hwnd, ActionID, ActionData); | |
| } | |
| /*********************************************************************** | |
| * WTHelperGetProvSignerFromChain (WINTRUST.@) | |
| */ | |
| CRYPT_PROVIDER_SGNR * WINAPI WTHelperGetProvSignerFromChain( | |
| CRYPT_PROVIDER_DATA *pProvData, DWORD idxSigner, BOOL fCounterSigner, | |
| DWORD idxCounterSigner) | |
| { | |
| CRYPT_PROVIDER_SGNR *sgnr; | |
| TRACE("(%p %d %d %d)\n", pProvData, idxSigner, fCounterSigner, | |
| idxCounterSigner); | |
| if (idxSigner >= pProvData->csSigners || !pProvData->pasSigners) | |
| return NULL; | |
| sgnr = &pProvData->pasSigners[idxSigner]; | |
| if (fCounterSigner) | |
| { | |
| if (idxCounterSigner >= sgnr->csCounterSigners || | |
| !sgnr->pasCounterSigners) | |
| return NULL; | |
| sgnr = &sgnr->pasCounterSigners[idxCounterSigner]; | |
| } | |
| TRACE("returning %p\n", sgnr); | |
| return sgnr; | |
| } | |
| /*********************************************************************** | |
| * WTHelperGetProvCertFromChain (WINTRUST.@) | |
| */ | |
| CRYPT_PROVIDER_CERT * WINAPI WTHelperGetProvCertFromChain( | |
| CRYPT_PROVIDER_SGNR *pSgnr, DWORD idxCert) | |
| { | |
| CRYPT_PROVIDER_CERT *cert; | |
| TRACE("(%p %d)\n", pSgnr, idxCert); | |
| if (idxCert >= pSgnr->csCertChain || !pSgnr->pasCertChain) | |
| return NULL; | |
| cert = &pSgnr->pasCertChain[idxCert]; | |
| TRACE("returning %p\n", cert); | |
| return cert; | |
| } | |
| CRYPT_PROVIDER_PRIVDATA *WINAPI WTHelperGetProvPrivateDataFromChain( | |
| CRYPT_PROVIDER_DATA* pProvData, | |
| GUID* pgProviderID) | |
| { | |
| CRYPT_PROVIDER_PRIVDATA *privdata = NULL; | |
| DWORD i; | |
| TRACE("(%p, %s)\n", pProvData, debugstr_guid(pgProviderID)); | |
| for (i = 0; i < pProvData->csProvPrivData; i++) | |
| if (IsEqualGUID(pgProviderID, &pProvData->pasProvPrivData[i].gProviderID)) | |
| { | |
| privdata = &pProvData->pasProvPrivData[i]; | |
| break; | |
| } | |
| return privdata; | |
| } | |
| /*********************************************************************** | |
| * WTHelperProvDataFromStateData (WINTRUST.@) | |
| */ | |
| CRYPT_PROVIDER_DATA * WINAPI WTHelperProvDataFromStateData(HANDLE hStateData) | |
| { | |
| TRACE("%p\n", hStateData); | |
| return hStateData; | |
| } | |
| /*********************************************************************** | |
| * WTHelperGetFileName(WINTRUST.@) | |
| */ | |
| LPCWSTR WINAPI WTHelperGetFileName(WINTRUST_DATA *data) | |
| { | |
| TRACE("%p\n",data); | |
| if (data->dwUnionChoice == WTD_CHOICE_FILE) | |
| return data->u.pFile->pcwszFilePath; | |
| else | |
| return NULL; | |
| } | |
| /*********************************************************************** | |
| * WTHelperGetFileHandle(WINTRUST.@) | |
| */ | |
| HANDLE WINAPI WTHelperGetFileHandle(WINTRUST_DATA *data) | |
| { | |
| TRACE("%p\n",data); | |
| if (data->dwUnionChoice == WTD_CHOICE_FILE) | |
| return data->u.pFile->hFile; | |
| else | |
| return INVALID_HANDLE_VALUE; | |
| } | |
| static BOOL WINAPI WINTRUST_enumUsages(PCCRYPT_OID_INFO pInfo, void *pvArg) | |
| { | |
| PCCRYPT_OID_INFO **usages = pvArg; | |
| DWORD cUsages; | |
| BOOL ret; | |
| if (!*usages) | |
| { | |
| cUsages = 0; | |
| *usages = WINTRUST_Alloc(2 * sizeof(PCCRYPT_OID_INFO)); | |
| } | |
| else | |
| { | |
| PCCRYPT_OID_INFO *ptr; | |
| /* Count the existing usages. | |
| * FIXME: make sure the new usage doesn't duplicate any in the list? | |
| */ | |
| for (cUsages = 0, ptr = *usages; *ptr; ptr++, cUsages++) | |
| ; | |
| *usages = WINTRUST_ReAlloc(*usages, | |
| (cUsages + 2) * sizeof(PCCRYPT_OID_INFO)); | |
| } | |
| if (*usages) | |
| { | |
| (*usages)[cUsages] = pInfo; | |
| (*usages)[cUsages + 1] = NULL; | |
| ret = TRUE; | |
| } | |
| else | |
| { | |
| SetLastError(ERROR_OUTOFMEMORY); | |
| ret = FALSE; | |
| } | |
| return ret; | |
| } | |
| /*********************************************************************** | |
| * WTHelperGetKnownUsages(WINTRUST.@) | |
| * | |
| * Enumerates the known enhanced key usages as an array of PCCRYPT_OID_INFOs. | |
| * | |
| * PARAMS | |
| * action [In] 1 => allocate and return known usages, 2 => free previously | |
| * allocated usages. | |
| * usages [In/Out] If action == 1, *usages is set to an array of | |
| * PCCRYPT_OID_INFO *. The array is terminated with a NULL | |
| * pointer. | |
| * If action == 2, *usages is freed. | |
| * | |
| * RETURNS | |
| * TRUE on success, FALSE on failure. | |
| */ | |
| BOOL WINAPI WTHelperGetKnownUsages(DWORD action, PCCRYPT_OID_INFO **usages) | |
| { | |
| BOOL ret; | |
| TRACE("(%d, %p)\n", action, usages); | |
| if (!usages) | |
| { | |
| SetLastError(ERROR_INVALID_PARAMETER); | |
| return FALSE; | |
| } | |
| if (action == 1) | |
| { | |
| *usages = NULL; | |
| ret = CryptEnumOIDInfo(CRYPT_ENHKEY_USAGE_OID_GROUP_ID, 0, usages, | |
| WINTRUST_enumUsages); | |
| } | |
| else if (action == 2) | |
| { | |
| WINTRUST_Free(*usages); | |
| *usages = NULL; | |
| ret = TRUE; | |
| } | |
| else | |
| { | |
| WARN("unknown action %d\n", action); | |
| SetLastError(ERROR_INVALID_PARAMETER); | |
| ret = FALSE; | |
| } | |
| return ret; | |
| } | |
| static const WCHAR Software_Publishing[] = { | |
| 'S','o','f','t','w','a','r','e','\\', | |
| 'M','i','c','r','o','s','o','f','t','\\', | |
| 'W','i','n','d','o','w','s','\\', | |
| 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', | |
| 'W','i','n','t','r','u','s','t','\\', | |
| 'T','r','u','s','t',' ','P','r','o','v','i','d','e','r','s','\\', | |
| 'S','o','f','t','w','a','r','e',' ', | |
| 'P','u','b','l','i','s','h','i','n','g',0 }; | |
| static const WCHAR State[] = { 'S','t','a','t','e',0 }; | |
| /*********************************************************************** | |
| * WintrustGetRegPolicyFlags (WINTRUST.@) | |
| */ | |
| void WINAPI WintrustGetRegPolicyFlags( DWORD* pdwPolicyFlags ) | |
| { | |
| HKEY key; | |
| LONG r; | |
| TRACE("%p\n", pdwPolicyFlags); | |
| *pdwPolicyFlags = 0; | |
| r = RegCreateKeyExW(HKEY_CURRENT_USER, Software_Publishing, 0, NULL, 0, | |
| KEY_READ, NULL, &key, NULL); | |
| if (!r) | |
| { | |
| DWORD size = sizeof(DWORD); | |
| r = RegQueryValueExW(key, State, NULL, NULL, (LPBYTE)pdwPolicyFlags, | |
| &size); | |
| RegCloseKey(key); | |
| if (r) | |
| { | |
| /* Failed to query, create and return default value */ | |
| *pdwPolicyFlags = WTPF_IGNOREREVOCATIONONTS | | |
| WTPF_OFFLINEOKNBU_COM | | |
| WTPF_OFFLINEOKNBU_IND | | |
| WTPF_OFFLINEOK_COM | | |
| WTPF_OFFLINEOK_IND; | |
| WintrustSetRegPolicyFlags(*pdwPolicyFlags); | |
| } | |
| } | |
| } | |
| /*********************************************************************** | |
| * WintrustSetRegPolicyFlags (WINTRUST.@) | |
| */ | |
| BOOL WINAPI WintrustSetRegPolicyFlags( DWORD dwPolicyFlags) | |
| { | |
| HKEY key; | |
| LONG r; | |
| TRACE("%x\n", dwPolicyFlags); | |
| r = RegCreateKeyExW(HKEY_CURRENT_USER, Software_Publishing, 0, | |
| NULL, 0, KEY_WRITE, NULL, &key, NULL); | |
| if (!r) | |
| { | |
| r = RegSetValueExW(key, State, 0, REG_DWORD, (LPBYTE)&dwPolicyFlags, | |
| sizeof(DWORD)); | |
| RegCloseKey(key); | |
| } | |
| if (r) SetLastError(r); | |
| return r == ERROR_SUCCESS; | |
| } | |
| /* Utility functions */ | |
| BOOL WINAPI WINTRUST_AddStore(CRYPT_PROVIDER_DATA *data, HCERTSTORE store) | |
| { | |
| BOOL ret = FALSE; | |
| TRACE("(%p, %p)\n", data, store); | |
| if (data->chStores) | |
| data->pahStores = WINTRUST_ReAlloc(data->pahStores, | |
| (data->chStores + 1) * sizeof(HCERTSTORE)); | |
| else | |
| { | |
| data->pahStores = WINTRUST_Alloc(sizeof(HCERTSTORE)); | |
| data->chStores = 0; | |
| } | |
| if (data->pahStores) | |
| { | |
| data->pahStores[data->chStores++] = CertDuplicateStore(store); | |
| ret = TRUE; | |
| } | |
| else | |
| SetLastError(ERROR_OUTOFMEMORY); | |
| return ret; | |
| } | |
| BOOL WINAPI WINTRUST_AddSgnr(CRYPT_PROVIDER_DATA *data, | |
| BOOL fCounterSigner, DWORD idxSigner, CRYPT_PROVIDER_SGNR *sgnr) | |
| { | |
| BOOL ret = FALSE; | |
| TRACE("(%p, %d, %d, %p)\n", data, fCounterSigner, idxSigner, sgnr); | |
| if (sgnr->cbStruct > sizeof(CRYPT_PROVIDER_SGNR)) | |
| { | |
| SetLastError(ERROR_INVALID_PARAMETER); | |
| return FALSE; | |
| } | |
| if (fCounterSigner) | |
| { | |
| FIXME("unimplemented for counter signers\n"); | |
| SetLastError(ERROR_INVALID_PARAMETER); | |
| return FALSE; | |
| } | |
| if (data->csSigners) | |
| data->pasSigners = WINTRUST_ReAlloc(data->pasSigners, | |
| (data->csSigners + 1) * sizeof(CRYPT_PROVIDER_SGNR)); | |
| else | |
| { | |
| data->pasSigners = WINTRUST_Alloc(sizeof(CRYPT_PROVIDER_SGNR)); | |
| data->csSigners = 0; | |
| } | |
| if (data->pasSigners) | |
| { | |
| if (idxSigner < data->csSigners) | |
| memmove(&data->pasSigners[idxSigner], | |
| &data->pasSigners[idxSigner + 1], | |
| (data->csSigners - idxSigner) * sizeof(CRYPT_PROVIDER_SGNR)); | |
| ret = TRUE; | |
| if (sgnr->cbStruct == sizeof(CRYPT_PROVIDER_SGNR)) | |
| { | |
| /* The PSDK says psSigner should be allocated using pfnAlloc, but | |
| * it doesn't say anything about ownership. Since callers are | |
| * internal, assume ownership is passed, and just store the | |
| * pointer. | |
| */ | |
| memcpy(&data->pasSigners[idxSigner], sgnr, | |
| sizeof(CRYPT_PROVIDER_SGNR)); | |
| } | |
| else | |
| memset(&data->pasSigners[idxSigner], 0, | |
| sizeof(CRYPT_PROVIDER_SGNR)); | |
| data->csSigners++; | |
| } | |
| else | |
| SetLastError(ERROR_OUTOFMEMORY); | |
| return ret; | |
| } | |
| BOOL WINAPI WINTRUST_AddCert(CRYPT_PROVIDER_DATA *data, DWORD idxSigner, | |
| BOOL fCounterSigner, DWORD idxCounterSigner, PCCERT_CONTEXT pCert2Add) | |
| { | |
| BOOL ret = FALSE; | |
| TRACE("(%p, %d, %d, %d, %p)\n", data, idxSigner, fCounterSigner, | |
| idxSigner, pCert2Add); | |
| if (fCounterSigner) | |
| { | |
| FIXME("unimplemented for counter signers\n"); | |
| SetLastError(ERROR_INVALID_PARAMETER); | |
| return FALSE; | |
| } | |
| if (data->pasSigners[idxSigner].csCertChain) | |
| data->pasSigners[idxSigner].pasCertChain = | |
| WINTRUST_ReAlloc(data->pasSigners[idxSigner].pasCertChain, | |
| (data->pasSigners[idxSigner].csCertChain + 1) * | |
| sizeof(CRYPT_PROVIDER_CERT)); | |
| else | |
| { | |
| data->pasSigners[idxSigner].pasCertChain = | |
| WINTRUST_Alloc(sizeof(CRYPT_PROVIDER_CERT)); | |
| data->pasSigners[idxSigner].csCertChain = 0; | |
| } | |
| if (data->pasSigners[idxSigner].pasCertChain) | |
| { | |
| CRYPT_PROVIDER_CERT *cert = &data->pasSigners[idxSigner].pasCertChain[ | |
| data->pasSigners[idxSigner].csCertChain]; | |
| cert->cbStruct = sizeof(CRYPT_PROVIDER_CERT); | |
| cert->pCert = CertDuplicateCertificateContext(pCert2Add); | |
| data->pasSigners[idxSigner].csCertChain++; | |
| ret = TRUE; | |
| } | |
| else | |
| SetLastError(ERROR_OUTOFMEMORY); | |
| return ret; | |
| } | |
| BOOL WINAPI WINTRUST_AddPrivData(CRYPT_PROVIDER_DATA *data, | |
| CRYPT_PROVIDER_PRIVDATA *pPrivData2Add) | |
| { | |
| BOOL ret = FALSE; | |
| TRACE("(%p, %p)\n", data, pPrivData2Add); | |
| if (pPrivData2Add->cbStruct > sizeof(CRYPT_PROVIDER_PRIVDATA)) | |
| { | |
| SetLastError(ERROR_INVALID_PARAMETER); | |
| WARN("invalid struct size\n"); | |
| return FALSE; | |
| } | |
| if (data->csProvPrivData) | |
| data->pasProvPrivData = WINTRUST_ReAlloc(data->pasProvPrivData, | |
| (data->csProvPrivData + 1) * sizeof(CRYPT_PROVIDER_SGNR)); | |
| else | |
| { | |
| data->pasProvPrivData = WINTRUST_Alloc(sizeof(CRYPT_PROVIDER_SGNR)); | |
| data->csProvPrivData = 0; | |
| } | |
| if (data->pasProvPrivData) | |
| { | |
| DWORD i; | |
| for (i = 0; i < data->csProvPrivData; i++) | |
| if (IsEqualGUID(&pPrivData2Add->gProviderID, &data->pasProvPrivData[i])) | |
| break; | |
| data->pasProvPrivData[i] = *pPrivData2Add; | |
| if (i == data->csProvPrivData) | |
| data->csProvPrivData++; | |
| } | |
| else | |
| SetLastError(ERROR_OUTOFMEMORY); | |
| return ret; | |
| } | |
| /*********************************************************************** | |
| * OpenPersonalTrustDBDialog (WINTRUST.@) | |
| * | |
| * Opens the certificate manager dialog, showing only the stores that | |
| * contain trusted software publishers. | |
| * | |
| * PARAMS | |
| * hwnd [I] handle of parent window | |
| * | |
| * RETURNS | |
| * TRUE if the dialog could be opened, FALSE if not. | |
| */ | |
| BOOL WINAPI OpenPersonalTrustDBDialog(HWND hwnd) | |
| { | |
| CRYPTUI_CERT_MGR_STRUCT uiCertMgr; | |
| uiCertMgr.dwSize = sizeof(uiCertMgr); | |
| uiCertMgr.hwndParent = hwnd; | |
| uiCertMgr.dwFlags = CRYPTUI_CERT_MGR_PUBLISHER_TAB; | |
| uiCertMgr.pwszTitle = NULL; | |
| uiCertMgr.pszInitUsageOID = NULL; | |
| return CryptUIDlgCertMgr(&uiCertMgr); | |
| } | |
| /*********************************************************************** | |
| * WTHelperCertCheckValidSignature | |
| */ | |
| HRESULT WINAPI WTHelperCertCheckValidSignature(CRYPT_PROVIDER_DATA *pProvData) | |
| { | |
| FIXME("Stub\n"); | |
| return S_OK; | |
| } |