//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1996 - 1999 // // File: testprov.cpp // // Contents: Microsoft Internet Security Trust Provider // // Functions: DllRegisterServer // DllUnregisterServer // TestprovInitialize // TestprovObjectProv // TestprovSigProv // TestprovCertCheckProv // TestprovFinalProv // TestprovCleanup // TestprovTester // // History: 28-Jul-1997 pberkman created // //-------------------------------------------------------------------------- #include #include #include #include // structures and APIs #include "wintrustp.h" // structures and APIs #include // reference for Authenticode #include // ui module DACUI.DLL #include "testprov.h" // my stuff HRESULT CallUI(CRYPT_PROVIDER_DATA *pProvData, DWORD dwError); DWORD GetErrorBasedOnStepErrors(CRYPT_PROVIDER_DATA *pProvData); HRESULT CheckCertificateChain(CRYPT_PROVIDER_DATA *pProvData, CRYPT_PROVIDER_SGNR *pProvSgnr); HRESULT CheckRevocation(CRYPT_PROVIDER_DATA *pProvData, CRYPT_PROVIDER_SGNR *pSgnr); BOOL CheckCertAnyUnknownCriticalExtensions(CRYPT_PROVIDER_DATA *pProvData, PCERT_INFO pCertInfo); ////////////////////////////////////////////////////////////////////////////// // // DllRegisterServer //---------------------------------------------------------------------------- // Register "my" provider // STDAPI DllRegisterServer(void) { GUID gTestprov = TESTPROV_ACTION_TEST; CRYPT_REGISTER_ACTIONID sRegAID; memset(&sRegAID, 0x00, sizeof(CRYPT_REGISTER_ACTIONID)); sRegAID.cbStruct = sizeof(CRYPT_REGISTER_ACTIONID); sRegAID.sInitProvider.cbStruct = sizeof(CRYPT_TRUST_REG_ENTRY); sRegAID.sInitProvider.pwszDLLName = TP_DLL_NAME; sRegAID.sInitProvider.pwszFunctionName = TP_INIT_FUNCTION; sRegAID.sObjectProvider.cbStruct = sizeof(CRYPT_TRUST_REG_ENTRY); sRegAID.sObjectProvider.pwszDLLName = TP_DLL_NAME; sRegAID.sObjectProvider.pwszFunctionName = TP_OBJTRUST_FUNCTION; sRegAID.sSignatureProvider.cbStruct = sizeof(CRYPT_TRUST_REG_ENTRY); sRegAID.sSignatureProvider.pwszDLLName = TP_DLL_NAME; sRegAID.sSignatureProvider.pwszFunctionName = TP_SIGTRUST_FUNCTION; sRegAID.sCertificateProvider.cbStruct = sizeof(CRYPT_TRUST_REG_ENTRY); sRegAID.sCertificateProvider.pwszDLLName = WT_PROVIDER_DLL_NAME; // set to wintrust.dll sRegAID.sCertificateProvider.pwszFunctionName = WT_PROVIDER_CERTTRUST_FUNCTION; // use wintrust's standard! sRegAID.sCertificatePolicyProvider.cbStruct = sizeof(CRYPT_TRUST_REG_ENTRY); sRegAID.sCertificatePolicyProvider.pwszDLLName = TP_DLL_NAME; sRegAID.sCertificatePolicyProvider.pwszFunctionName = TP_CHKCERT_FUNCTION; sRegAID.sFinalPolicyProvider.cbStruct = sizeof(CRYPT_TRUST_REG_ENTRY); sRegAID.sFinalPolicyProvider.pwszDLLName = TP_DLL_NAME; sRegAID.sFinalPolicyProvider.pwszFunctionName = TP_FINALPOLICY_FUNCTION; sRegAID.sCleanupProvider.cbStruct = sizeof(CRYPT_TRUST_REG_ENTRY); sRegAID.sCleanupProvider.pwszDLLName = TP_DLL_NAME; sRegAID.sCleanupProvider.pwszFunctionName = TP_CLEANUPPOLICY_FUNCTION; sRegAID.sTestPolicyProvider.cbStruct = sizeof(CRYPT_TRUST_REG_ENTRY); sRegAID.sTestPolicyProvider.pwszDLLName = TP_DLL_NAME; sRegAID.sTestPolicyProvider.pwszFunctionName = TP_TESTDUMPPOLICY_FUNCTION_TEST; if (WintrustAddActionID(&gTestprov, 0, &sRegAID)) { return(S_OK); } return(S_FALSE); } ////////////////////////////////////////////////////////////////////////////// // // DllUnregisterServer //---------------------------------------------------------------------------- // unregisters "my" provider // STDAPI DllUnregisterServer(void) { GUID gTestprov = TESTPROV_ACTION_TEST; WintrustRemoveActionID(&gTestprov); return(S_OK); } ////////////////////////////////////////////////////////////////////////////// // // Initialization Provider function: testprovInitialize //---------------------------------------------------------------------------- // allocates and sets up "my" data. // HRESULT WINAPI TestprovInitialize(CRYPT_PROVIDER_DATA *pProvData) { if (!(pProvData->padwTrustStepErrors) || (pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_WVTINIT] != ERROR_SUCCESS)) { return(S_FALSE); } // // add our private data to the array... // CRYPT_PROVIDER_PRIVDATA sMyData; TESTPROV_PRIVATE_DATA *pMyData; GUID gMyId = TESTPROV_ACTION_TEST; memset(&sMyData, 0x00, sizeof(CRYPT_PROVIDER_PRIVDATA)); sMyData.cbStruct = sizeof(CRYPT_PROVIDER_PRIVDATA); memcpy(&sMyData.gProviderID, &gMyId, sizeof(GUID)); if (!(sMyData.pvProvData = pProvData->psPfns->pfnAlloc(sizeof(TESTPROV_PRIVATE_DATA)))) { pProvData->dwError = GetLastError(); pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_INITPROV] = TRUST_E_SYSTEM_ERROR; return(S_FALSE); } memset(sMyData.pvProvData, 0x00, sizeof(TESTPROV_PRIVATE_DATA)); pMyData = (TESTPROV_PRIVATE_DATA *)sMyData.pvProvData; pMyData->cbStruct = sizeof(TESTPROV_PRIVATE_DATA); // // fill in the Authenticode Functions // GUID gSP = WINTRUST_ACTION_TRUSTPROVIDER_TEST; pMyData->sAuthenticodePfns.cbStruct = sizeof(CRYPT_PROVIDER_FUNCTIONS_ORMORE); if (!(WintrustLoadFunctionPointers(&gSP, (CRYPT_PROVIDER_FUNCTIONS *)&pMyData->sAuthenticodePfns))) { pProvData->psPfns->pfnFree(sMyData.pvProvData); pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_INITPROV] = TRUST_E_PROVIDER_UNKNOWN; return(S_FALSE); } // // add my data to the chain! // pProvData->psPfns->pfnAddPrivData2Chain(pProvData, &sMyData); return(pMyData->sAuthenticodePfns.pfnInitialize(pProvData)); } ////////////////////////////////////////////////////////////////////////////// // // Object Provider function: TestprovObjectProv //---------------------------------------------------------------------------- // we don't do anything here -- we're not authenticating a signed object. // HRESULT WINAPI TestprovObjectProv(CRYPT_PROVIDER_DATA *pProvData) { if (!(pProvData->padwTrustStepErrors) || (pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_WVTINIT] != ERROR_SUCCESS) || (pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_INITPROV] != ERROR_SUCCESS)) { return(S_FALSE); } CRYPT_PROVIDER_PRIVDATA *pPrivData; TESTPROV_PRIVATE_DATA *pMyData; GUID gMyId = TESTPROV_ACTION_TEST; if (!(pPrivData = WTHelperGetProvPrivateDataFromChain(pProvData, &gMyId))) { pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_INITPROV] = ERROR_INVALID_PARAMETER; return(S_FALSE); } pMyData = (TESTPROV_PRIVATE_DATA *)pPrivData->pvProvData; // // we are verifying a low-level type choice (eg: cert or signer) // switch (pProvData->pWintrustData->dwUnionChoice) { case WTD_CHOICE_CERT: case WTD_CHOICE_SIGNER: break; default: return(pMyData->sAuthenticodePfns.pfnObjectTrust(pProvData)); } // // no object to be verified here! // return(ERROR_SUCCESS); } ////////////////////////////////////////////////////////////////////////////// // // Signature Provider function: TestprovInitialize //---------------------------------------------------------------------------- // We are going to let Authenticode take care of most of this stuff! // HRESULT WINAPI TestprovSigProv(CRYPT_PROVIDER_DATA *pProvData) { if (!(pProvData->padwTrustStepErrors) || (pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_WVTINIT] != ERROR_SUCCESS) || (pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_INITPROV] != ERROR_SUCCESS) || (pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] != ERROR_SUCCESS)) { return(S_FALSE); } CRYPT_PROVIDER_PRIVDATA *pPrivData; TESTPROV_PRIVATE_DATA *pMyData; GUID gMyId = TESTPROV_ACTION_TEST; if (!(pPrivData = WTHelperGetProvPrivateDataFromChain(pProvData, &gMyId))) { pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_INITPROV] = ERROR_INVALID_PARAMETER; return(S_FALSE); } pMyData = (TESTPROV_PRIVATE_DATA *)pPrivData->pvProvData; // // we are verifying a low-level type choice (eg: cert or signer) // switch (pProvData->pWintrustData->dwUnionChoice) { case WTD_CHOICE_CERT: case WTD_CHOICE_SIGNER: break; default: return(pMyData->sAuthenticodePfns.pfnSignatureTrust(pProvData)); } if (pMyData->sAuthenticodePfns.pfnSignatureTrust) { return(pMyData->sAuthenticodePfns.pfnSignatureTrust(pProvData)); } return(S_FALSE); } ////////////////////////////////////////////////////////////////////////////// // // Certificate Check Provider function: TestprovCertCheckProv //---------------------------------------------------------------------------- // just check basic stuff about a certificate. return FALSE to stop // building the chain, TRUE to continue. // BOOL WINAPI TestprovCheckCertProv(CRYPT_PROVIDER_DATA *pProvData, DWORD idxSigner, BOOL fCounterSignerChain, DWORD idxCounterSigner) { CRYPT_PROVIDER_SGNR *pSgnr; CRYPT_PROVIDER_CERT *pCert; PCCERT_CONTEXT pCertContext; pSgnr = WTHelperGetProvSignerFromChain(pProvData, idxSigner, fCounterSignerChain, idxCounterSigner); pCert = WTHelperGetProvCertFromChain(pSgnr, pSgnr->csCertChain - 1); pCert->fTrustedRoot = FALSE; // // only self signed certificates in the root store are "trusted" roots // if (pCert->fSelfSigned) { pCertContext = pCert->pCert; if (pCertContext) { if (pProvData->chStores > 0) { if (pCertContext->hCertStore == pProvData->pahStores[0]) { // // it's in the root store! // pCert->fTrustedRoot = TRUE; return(FALSE); } } if (!(pCert->fTrustedRoot)) { if (pCert->fTestCert) { // // check the policy flags (setreg.exe) to see if we trust // the test root. // if (pProvData->dwRegPolicySettings & WTPF_TRUSTTEST) { pCert->fTrustedRoot = TRUE; return(FALSE); } } } } // // the cert is self-signed... we need to stop regardless // return(FALSE); } return(TRUE); } ////////////////////////////////////////////////////////////////////////////// // // Final Policy Provider function: TestprovFinalProv //---------------------------------------------------------------------------- // Check the outcome of the previous functions and display UI based on this. // HRESULT WINAPI TestprovFinalProv(CRYPT_PROVIDER_DATA *pProvData) { CRYPT_PROVIDER_SGNR *pSigner; DWORD dwError; if ((dwError = GetErrorBasedOnStepErrors(pProvData)) != ERROR_SUCCESS) { return(CallUI(pProvData, dwError)); } pSigner = WTHelperGetProvSignerFromChain(pProvData, 0, FALSE, 0); if ((dwError = CheckCertificateChain(pProvData, pSigner)) != ERROR_SUCCESS) { return(CallUI(pProvData, dwError)); } return(CallUI(pProvData, dwError)); } ////////////////////////////////////////////////////////////////////////////// // // Cleanup Provider function: TestprovCleanup //---------------------------------------------------------------------------- // call all other policy provider cleanup functions, then, free "my" stuff. // HRESULT WINAPI TestprovCleanup(CRYPT_PROVIDER_DATA *pProvData) { GUID gMyId = TESTPROV_ACTION_TEST; CRYPT_PROVIDER_PRIVDATA *pPrivData; TESTPROV_PRIVATE_DATA *pMyData; // // we have used the Authenticode Provider. we need to call its // cleanup routine just in case.... so, get our private data // which will have the Authenticode functions in our structure.. // if (!(pPrivData = WTHelperGetProvPrivateDataFromChain(pProvData, &gMyId))) { return(S_FALSE); } if (!(pPrivData->pvProvData)) { return(S_FALSE); } pMyData = (TESTPROV_PRIVATE_DATA *)pPrivData->pvProvData; if (pMyData->sAuthenticodePfns.pfnCleanupPolicy) { pMyData->sAuthenticodePfns.pfnCleanupPolicy(pProvData); } // // now, we need to delete our private data // pProvData->psPfns->pfnFree(pPrivData->pvProvData); pPrivData->cbProvData = 0; pPrivData->pvProvData = NULL; return(ERROR_SUCCESS); } ////////////////////////////////////////////////////////////////////////////// // // Test Provider function: TestprovTester //---------------------------------------------------------------------------- // In here, we are going to check an environment variable and if set we // will call Authenticode's "dump" function. // HRESULT WINAPI TestprovTester(CRYPT_PROVIDER_DATA *pProvData) { BYTE abEnv[MAX_PATH + 1]; CRYPT_PROVIDER_PRIVDATA *pPrivData; TESTPROV_PRIVATE_DATA *pMyData; GUID gMyId = TESTPROV_ACTION_TEST; abEnv[0] = NULL; if (GetEnvironmentVariable("TestProvUseDump", (char *)&abEnv[0], MAX_PATH) < 1) { return(ERROR_SUCCESS); } if ((abEnv[0] != '1') && (toupper(abEnv[0]) != 'T')) { return(ERROR_SUCCESS); } if (!(pPrivData = WTHelperGetProvPrivateDataFromChain(pProvData, &gMyId))) { pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_INITPROV] = ERROR_INVALID_PARAMETER; return(S_FALSE); } pMyData = (TESTPROV_PRIVATE_DATA *)pPrivData->pvProvData; if (pMyData->sAuthenticodePfns.pfnTestFinalPolicy) { return(pMyData->sAuthenticodePfns.pfnTestFinalPolicy(pProvData)); } return(S_FALSE); } /////////////////////////////////////////////////////////////////////////////////// // // Local Functions // /////////////////////////////////////////////////////////////////////////////////// DWORD GetErrorBasedOnStepErrors(CRYPT_PROVIDER_DATA *pProvData) { // // initial allocation of the step errors? // if (!(pProvData->padwTrustStepErrors)) { return(ERROR_NOT_ENOUGH_MEMORY); } // // did we fail in one of the last steps? // if (pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_INITPROV] != 0) { return(pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_INITPROV]); } if (pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] != 0) { return(pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV]); } if (pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_SIGPROV] != 0) { return(pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_SIGPROV]); } if (pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_CERTPROV] != 0) { return(pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_CERTPROV]); } if (pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_CERTCHKPROV] != 0) { return(pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_CERTCHKPROV]); } if (pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_POLICYPROV] != 0) { return(pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_POLICYPROV]); } return(ERROR_SUCCESS); } HRESULT CheckCertificateChain(CRYPT_PROVIDER_DATA *pProvData, CRYPT_PROVIDER_SGNR *pProvSgnr) { CRYPT_PROVIDER_CERT *pCert; for (int i = 0; i < (int)pProvSgnr->csCertChain; i++) { pCert = WTHelperGetProvCertFromChain(pProvSgnr, i); if (!(pProvData->dwRegPolicySettings & WTPF_IGNOREEXPIRATION)) { // // this check was done in the Certificate Provider, however, it may not have passed // because all its looking for is a confidence level and didn't check the end.. // if (CertVerifyTimeValidity(&pProvSgnr->sftVerifyAsOf, pCert->pCert->pCertInfo) != 0) { pCert->dwError = CERT_E_EXPIRED; return(pCert->dwError); } } // // check any unknown critical extensions // if (!(CheckCertAnyUnknownCriticalExtensions(pProvData, pCert->pCert->pCertInfo))) { pCert->dwError = CERT_E_MALFORMED; return(pCert->dwError); } if ((i + 1) < (int)pProvSgnr->csCertChain) { // // check time nesting... // if (!(pCert->dwConfidence & CERT_CONFIDENCE_TIMENEST)) { pCert->dwError = CERT_E_VALIDITYPERIODNESTING; return(pCert->dwError); } } } if (!(pProvData->dwRegPolicySettings & WTPF_IGNOREREVOKATION)) { // root cert is test? pCert = WTHelperGetProvCertFromChain(pProvSgnr, pProvSgnr->csCertChain - 1); if (pCert) { if (!(pCert->fTestCert)) { // // if the caller already told WVT to check, we don't have to! // if (pProvData->pWintrustData->fdwRevocationChecks != WTD_REVOKE_NONE) { // // not a test root, check signer cert for revocation // pCert = WTHelperGetProvCertFromChain(pProvSgnr, 0); return(CheckRevocation(pProvData, pProvSgnr)); } } } } return(ERROR_SUCCESS); } HRESULT CallUI(CRYPT_PROVIDER_DATA *pProvData, DWORD dwError) { HRESULT hr; HINSTANCE hModule; ACUI_INVOKE_INFO aii; pfnACUIProviderInvokeUI pfn; DWORD idxSigner; BOOL fTrusted; BOOL fCommercial; DWORD dwUIChoice; CRYPT_PROVIDER_SGNR *pRootSigner; CRYPT_PROVIDER_CERT *pPubCert; hr = E_NOTIMPL; pfn = NULL; fTrusted = FALSE; fCommercial = FALSE; idxSigner = 0; dwUIChoice = pProvData->pWintrustData->dwUIChoice; pRootSigner = WTHelperGetProvSignerFromChain(pProvData, 0, FALSE, 0); if ((dwUIChoice == WTD_UI_NONE) || ((dwUIChoice == WTD_UI_NOBAD) && (dwError != ERROR_SUCCESS)) || ((dwUIChoice == WTD_UI_NOGOOD) && (dwError == ERROR_SUCCESS))) { return(dwError); } // // Setup the UI invokation // memset(&aii, 0x00, sizeof(ACUI_INVOKE_INFO)); aii.cbSize = sizeof(ACUI_INVOKE_INFO); aii.hDisplay = pProvData->hWndParent; aii.pProvData = pProvData; aii.hrInvokeReason = dwError; aii.pwcsAltDisplayName = WTHelperGetFileName(pProvData->pWintrustData); // // Load the default authenticode UI. // if (hModule = LoadLibrary("dacui.dll")) { pfn = (pfnACUIProviderInvokeUI)GetProcAddress(hModule, "ACUIProviderInvokeUI"); } // // Invoke the UI // if (pfn) { hr = (*pfn)(&aii); } else if ((pProvData->pWintrustData->dwUIChoice != WTD_UI_NONE) && (pProvData->pWintrustData->dwUIChoice != WTD_UI_NOBAD)) { //TBDTBD!!! // // display error dialog "unable to load UI provider" // if (hr == E_NOTIMPL) { hr = TRUST_E_PROVIDER_UNKNOWN; } pProvData->dwError = hr; pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_UIPROV] = hr; } // // free the ui library // if (hModule) { FreeLibrary(hModule); } // // Return the appropriate code // return(hr); } static const char *rgpszKnownExtObjId[] = { szOID_AUTHORITY_KEY_IDENTIFIER, szOID_KEY_ATTRIBUTES, szOID_KEY_USAGE_RESTRICTION, szOID_SUBJECT_ALT_NAME, szOID_ISSUER_ALT_NAME, szOID_BASIC_CONSTRAINTS, SPC_COMMON_NAME_OBJID, SPC_SP_AGENCY_INFO_OBJID, SPC_MINIMAL_CRITERIA_OBJID, SPC_FINANCIAL_CRITERIA_OBJID, szOID_CERT_POLICIES, szOID_POLICY_MAPPINGS, szOID_SUBJECT_DIR_ATTRS, NULL }; BOOL CheckCertAnyUnknownCriticalExtensions(CRYPT_PROVIDER_DATA *pProvData, PCERT_INFO pCertInfo) { PCERT_EXTENSION pExt; DWORD cExt; const char **ppszObjId; const char *pszObjId; cExt = pCertInfo->cExtension; pExt = pCertInfo->rgExtension; for ( ; cExt > 0; cExt--, pExt++) { if (pExt->fCritical) { ppszObjId = (const char **)rgpszKnownExtObjId; while (pszObjId = *ppszObjId++) { if (strcmp(pszObjId, pExt->pszObjId) == 0) { break; } } if (!(pszObjId)) { return(FALSE); } } } return(TRUE); } HRESULT CheckRevocation(CRYPT_PROVIDER_DATA *pProvData, CRYPT_PROVIDER_SGNR *pSgnr) { CERT_REVOCATION_PARA sRevPara; CERT_REVOCATION_STATUS sRevStatus; PCERT_CONTEXT pasCertContext[1]; CRYPT_PROVIDER_CERT *pCert; memset(&sRevPara, 0x00, sizeof(CERT_REVOCATION_PARA)); sRevPara.cbSize = sizeof(CERT_REVOCATION_PARA); // issuer cert = 1 pCert = WTHelperGetProvCertFromChain(pSgnr, 1); sRevPara.pIssuerCert = pCert->pCert; memset(&sRevStatus, 0x00, sizeof(CERT_REVOCATION_STATUS)); sRevStatus.cbSize = sizeof(CERT_REVOCATION_STATUS); // publisher cert = 0 pCert = WTHelperGetProvCertFromChain(pSgnr, 0); pasCertContext[0] = (PCERT_CONTEXT)pCert->pCert; if (!(CertVerifyRevocation(pProvData->dwEncoding, CERT_CONTEXT_REVOCATION_TYPE, 1, (void **)pasCertContext, 0, // CERT_VERIFY_REV_CHAIN_FLAG, &sRevPara, &sRevStatus))) { pCert->dwRevokedReason = sRevStatus.dwReason; switch(sRevStatus.dwError) { case CRYPT_E_REVOKED: return(CERT_E_REVOKED); case CRYPT_E_NOT_IN_REVOCATION_DATABASE: return(ERROR_SUCCESS); case CRYPT_E_REVOCATION_OFFLINE: if ((pProvData->dwRegPolicySettings & WTPF_OFFLINEOK_IND) || (pProvData->dwRegPolicySettings & WTPF_OFFLINEOKNBU_IND)) { return(ERROR_SUCCESS); } return(CERT_E_REVOCATION_FAILURE); default: return(CERT_E_REVOCATION_FAILURE); } } return(ERROR_SUCCESS); }