//+-------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996 - 1999 // // File: exit.cpp // // Contents: CCertExitSample implementation // //--------------------------------------------------------------------------- #include "pch.cpp" #pragma hdrstop #include #include "celib.h" #include "exit.h" #include "module.h" BOOL fDebug = DBG_CERTSRV; #ifndef DBG_CERTSRV #error -- DBG_CERTSRV not defined! #endif #define ceEXITEVENTS \ (EXITEVENT_CERTDENIED | \ EXITEVENT_CERTISSUED | \ EXITEVENT_CERTPENDING | \ EXITEVENT_CERTRETRIEVEPENDING | \ EXITEVENT_CERTREVOKED | \ EXITEVENT_CRLISSUED | \ EXITEVENT_SHUTDOWN) extern HINSTANCE g_hInstance; HRESULT GetServerCallbackInterface( OUT ICertServerExit** ppServer, IN LONG Context) { HRESULT hr; if (NULL == ppServer) { hr = E_POINTER; _JumpError(hr, error, "Exit:NULL pointer"); } hr = CoCreateInstance( CLSID_CCertServerExit, NULL, // pUnkOuter CLSCTX_INPROC_SERVER, IID_ICertServerExit, (VOID **) ppServer); _JumpIfError(hr, error, "Exit:CoCreateInstance"); if (*ppServer == NULL) { hr = E_UNEXPECTED; _JumpError(hr, error, "Exit:NULL *ppServer"); } // only set context if nonzero if (0 != Context) { hr = (*ppServer)->SetContext(Context); _JumpIfError(hr, error, "Exit: SetContext"); } error: return(hr); } //+-------------------------------------------------------------------------- // CCertExitSample::~CCertExitSample -- destructor // // free memory associated with this instance //+-------------------------------------------------------------------------- CCertExitSample::~CCertExitSample() { if (NULL != m_strCAName) { SysFreeString(m_strCAName); } if (NULL != m_pwszRegStorageLoc) { LocalFree(m_pwszRegStorageLoc); } if (NULL != m_hExitKey) { RegCloseKey(m_hExitKey); } if (NULL != m_strDescription) { SysFreeString(m_strDescription); } } //+-------------------------------------------------------------------------- // CCertExitSample::Initialize -- initialize for a CA & return interesting Event Mask // // Returns S_OK on success. //+-------------------------------------------------------------------------- STDMETHODIMP CCertExitSample::Initialize( /* [in] */ BSTR const strConfig, /* [retval][out] */ LONG __RPC_FAR *pEventMask) { HRESULT hr = S_OK; DWORD cbbuf; DWORD dwType; ENUM_CATYPES CAType; ICertServerExit *pServer = NULL; VARIANT varValue; WCHAR sz[MAX_PATH]; VariantInit(&varValue); assert(wcslen(wsz_SAMPLE_DESCRIPTION) < ARRAYSIZE(sz)); wcsncpy(sz, wsz_SAMPLE_DESCRIPTION, ARRAYSIZE(sz)); sz[ARRAYSIZE(sz) - 1] = L'\0'; m_strDescription = SysAllocString(sz); if (NULL == m_strDescription) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Exit:SysAllocString"); } m_strCAName = SysAllocString(strConfig); if (NULL == m_strCAName) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Exit:SysAllocString"); } *pEventMask = ceEXITEVENTS; DBGPRINT((fDebug, "Exit:Initialize(%ws) ==> %x\n", m_strCAName, *pEventMask)); // get server callbacks hr = GetServerCallbackInterface(&pServer, 0); _JumpIfError(hr, error, "Exit:GetServerCallbackInterface"); // get storage location hr = exitGetProperty( pServer, FALSE, // fRequest wszPROPMODULEREGLOC, PROPTYPE_STRING, &varValue); _JumpIfErrorStr(hr, error, "Exit:exitGetProperty", wszPROPMODULEREGLOC); m_pwszRegStorageLoc = (LPWSTR)LocalAlloc(LMEM_FIXED, (wcslen(varValue.bstrVal)+1) *sizeof(WCHAR)); if (NULL == m_pwszRegStorageLoc) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Exit:LocalAlloc"); } wcscpy(m_pwszRegStorageLoc, varValue.bstrVal); VariantClear(&varValue); // get CA type hr = exitGetProperty( pServer, FALSE, // fRequest wszPROPCATYPE, PROPTYPE_LONG, &varValue); _JumpIfErrorStr(hr, error, "Exit:exitGetProperty", wszPROPCATYPE); CAType = (ENUM_CATYPES) varValue.lVal; VariantClear(&varValue); hr = RegOpenKeyEx( HKEY_LOCAL_MACHINE, m_pwszRegStorageLoc, 0, // dwReserved KEY_ENUMERATE_SUB_KEYS | KEY_EXECUTE | KEY_QUERY_VALUE, &m_hExitKey); if (S_OK != hr) { if ((HRESULT) ERROR_FILE_NOT_FOUND == hr) { hr = S_OK; goto error; } _JumpError(hr, error, "Exit:RegOpenKeyEx"); } hr = exitGetProperty( pServer, FALSE, // fRequest wszPROPCERTCOUNT, PROPTYPE_LONG, &varValue); _JumpIfErrorStr(hr, error, "Exit:exitGetProperty", wszPROPCERTCOUNT); m_cCACert = varValue.lVal; cbbuf = sizeof(m_dwExitPublishFlags); hr = RegQueryValueEx( m_hExitKey, wszREGCERTPUBLISHFLAGS, NULL, // lpdwReserved &dwType, (BYTE *) &m_dwExitPublishFlags, &cbbuf); if (S_OK != hr) { m_dwExitPublishFlags = 0; } hr = S_OK; error: VariantClear(&varValue); if (NULL != pServer) { pServer->Release(); } return(ceHError(hr)); } //+-------------------------------------------------------------------------- // CCertExitSample::_ExpandEnvironmentVariables -- Expand environment variables // //+-------------------------------------------------------------------------- HRESULT CCertExitSample::_ExpandEnvironmentVariables( IN WCHAR const *pwszIn, OUT WCHAR *pwszOut, IN DWORD cwcOut) { HRESULT hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW); WCHAR awcVar[MAX_PATH]; WCHAR const *pwszSrc; WCHAR *pwszDst; WCHAR *pwszDstEnd; WCHAR *pwszVar; DWORD cwc; pwszSrc = pwszIn; pwszDst = pwszOut; pwszDstEnd = &pwszOut[cwcOut]; while (L'\0' != (*pwszDst = *pwszSrc++)) { if ('%' == *pwszDst) { *pwszDst = L'\0'; pwszVar = awcVar; while (L'\0' != *pwszSrc) { if ('%' == *pwszSrc) { pwszSrc++; break; } *pwszVar++ = *pwszSrc++; if (pwszVar >= &awcVar[sizeof(awcVar)/sizeof(awcVar[0]) - 1]) { _JumpError(hr, error, "Exit:overflow 1"); } } *pwszVar = L'\0'; cwc = GetEnvironmentVariable(awcVar, pwszDst, SAFE_SUBTRACT_POINTERS(pwszDstEnd, pwszDst)); if (0 == cwc) { hr = ceHLastError(); _JumpError(hr, error, "Exit:GetEnvironmentVariable"); } if ((DWORD) (pwszDstEnd - pwszDst) <= cwc) { _JumpError(hr, error, "Exit:overflow 2"); } pwszDst += cwc; } else { pwszDst++; } if (pwszDst >= pwszDstEnd) { _JumpError(hr, error, "Exit:overflow 3"); } } hr = S_OK; error: return(hr); } HRESULT exitGetRequestAttribute( IN ICertServerExit *pServer, IN WCHAR const *pwszAttributeName, OUT BSTR *pstrOut) { HRESULT hr; BSTR strName = NULL; *pstrOut = NULL; strName = SysAllocString(pwszAttributeName); if (NULL == strName) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Exit:SysAllocString"); } hr = pServer->GetRequestAttribute(strName, pstrOut); _JumpIfErrorStr2( hr, error, "Exit:GetRequestAttribute", pwszAttributeName, CERTSRV_E_PROPERTY_EMPTY); error: if (NULL != strName) { SysFreeString(strName); } return(hr); } //+-------------------------------------------------------------------------- // CCertExitSample::_WriteCertToFile -- write binary certificate to a file // //+-------------------------------------------------------------------------- HRESULT CCertExitSample::_WriteCertToFile( IN ICertServerExit *pServer, IN BYTE const *pbCert, IN DWORD cbCert) { HRESULT hr; BSTR strCertFile = NULL; DWORD cbWritten; HANDLE hFile = INVALID_HANDLE_VALUE; WCHAR wszDir[MAX_PATH]; WCHAR *pwszPath = NULL; WCHAR wszFile[cwcDWORDSPRINTF+5]; //format "requestid.cer" VARIANT varRequestID; VariantInit(&varRequestID); hr = exitGetRequestAttribute(pServer, wszPROPEXITCERTFILE, &strCertFile); if (S_OK != hr) { DBGPRINT(( fDebug, "Exit:exitGetRequestAttribute(%ws): %x%hs\n", wszPROPEXITCERTFILE, hr, CERTSRV_E_PROPERTY_EMPTY == hr? " EMPTY VALUE" : "")); if (CERTSRV_E_PROPERTY_EMPTY == hr) { hr = S_OK; } goto error; } // build file name as "requestid.cer" hr = exitGetProperty( pServer, TRUE, // fRequest, wszPROPREQUESTREQUESTID, PROPTYPE_LONG, &varRequestID); _JumpIfErrorStr(hr, error, "Exit:exitGetProperty", wszPROPREQUESTREQUESTID); wsprintf(wszFile, L"%d.cer", V_I4(&varRequestID)); hr = _ExpandEnvironmentVariables( L"%SystemRoot%\\System32\\" wszCERTENROLLSHAREPATH L"\\", wszDir, ARRAYSIZE(wszDir)); _JumpIfError(hr, error, "_ExpandEnvironmentVariables"); hr = ceBuildPathAndExt(wszDir, wszFile, NULL, &pwszPath); _JumpIfError(hr, error, "ceBuildPathAndExt"); // open file & write binary cert out. hFile = CreateFile( pwszPath, GENERIC_WRITE, 0, // dwShareMode NULL, // lpSecurityAttributes CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); // hTemplateFile if (INVALID_HANDLE_VALUE == hFile) { hr = ceHLastError(); _JumpErrorStr(hr, error, "Exit:CreateFile", pwszPath); } if (!WriteFile(hFile, pbCert, cbCert, &cbWritten, NULL)) { hr = ceHLastError(); _JumpErrorStr(hr, error, "Exit:WriteFile", pwszPath); } if (cbWritten != cbCert) { hr = STG_E_WRITEFAULT; DBGPRINT(( fDebug, "Exit:WriteFile(%ws): attempted %x, actual %x bytes: %x\n", pwszPath, cbCert, cbWritten, hr)); goto error; } error: if (INVALID_HANDLE_VALUE != hFile) { CloseHandle(hFile); } if (NULL != pwszPath) { LocalFree(pwszPath); } if (NULL != strCertFile) { SysFreeString(strCertFile); } return(hr); } //+-------------------------------------------------------------------------- // CCertExitSample::_NotifyNewCert -- Notify the exit module of a new certificate // //+-------------------------------------------------------------------------- HRESULT CCertExitSample::_NotifyNewCert( /* [in] */ LONG Context) { HRESULT hr; VARIANT varCert; ICertServerExit *pServer = NULL; VariantInit(&varCert); // only call write fxns if server policy allows if (m_dwExitPublishFlags & EXITPUB_FILE) { hr = CoCreateInstance( CLSID_CCertServerExit, NULL, // pUnkOuter CLSCTX_INPROC_SERVER, IID_ICertServerExit, (VOID **) &pServer); _JumpIfError(hr, error, "Exit:CoCreateInstance"); hr = pServer->SetContext(Context); _JumpIfError(hr, error, "Exit:SetContext"); hr = exitGetProperty( pServer, FALSE, // fRequest, wszPROPRAWCERTIFICATE, PROPTYPE_BINARY, &varCert); _JumpIfErrorStr( hr, error, "Exit:exitGetProperty", wszPROPRAWCERTIFICATE); if (VT_BSTR != varCert.vt) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "Exit:BAD cert var type"); } hr = _WriteCertToFile( pServer, (BYTE const *) varCert.bstrVal, SysStringByteLen(varCert.bstrVal)); _JumpIfError(hr, error, "_WriteCertToFile"); } hr = S_OK; error: VariantClear(&varCert); if (NULL != pServer) { pServer->Release(); } return(hr); } //+-------------------------------------------------------------------------- // CCertExitSample::_NotifyCRLIssued -- Notify the exit module of a new certificate // //+-------------------------------------------------------------------------- HRESULT CCertExitSample::_NotifyCRLIssued( /* [in] */ LONG Context) { HRESULT hr; ICertServerExit *pServer = NULL; DWORD i; VARIANT varBaseCRL; VARIANT varDeltaCRL; BOOL fDeltaCRLsDisabled; VariantInit(&varBaseCRL); VariantInit(&varDeltaCRL); hr = CoCreateInstance( CLSID_CCertServerExit, NULL, // pUnkOuter CLSCTX_INPROC_SERVER, IID_ICertServerExit, (VOID **) &pServer); _JumpIfError(hr, error, "Exit:CoCreateInstance"); hr = pServer->SetContext(Context); _JumpIfError(hr, error, "Exit:SetContext"); hr = exitGetProperty( pServer, FALSE, // fRequest, wszPROPDELTACRLSDISABLED, PROPTYPE_LONG, &varBaseCRL); _JumpIfErrorStr( hr, error, "Exit:exitGetProperty", wszPROPDELTACRLSDISABLED); fDeltaCRLsDisabled = varBaseCRL.lVal; // How many CRLs are there? // Loop for each CRL for (i = 0; i < m_cCACert; i++) { // array size for wsprintf("%s.%u") #define MAX_CRL_PROP \ (max( \ max(ARRAYSIZE(wszPROPCRLSTATE), ARRAYSIZE(wszPROPRAWCRL)), \ ARRAYSIZE(wszPROPRAWDELTACRL)) + \ 1 + cwcDWORDSPRINTF) WCHAR wszCRLPROP[MAX_CRL_PROP]; // Verify the CRL State says we should update this CRL wsprintf(wszCRLPROP, wszPROPCRLSTATE L".%u", i); hr = exitGetProperty( pServer, FALSE, // fRequest, wszCRLPROP, PROPTYPE_LONG, &varBaseCRL); _JumpIfErrorStr(hr, error, "Exit:exitGetProperty", wszCRLPROP); if (CA_DISP_VALID != varBaseCRL.lVal) { continue; } // Grab the raw base CRL wsprintf(wszCRLPROP, wszPROPRAWCRL L".%u", i); hr = exitGetProperty( pServer, FALSE, // fRequest, wszCRLPROP, PROPTYPE_BINARY, &varBaseCRL); _JumpIfErrorStr(hr, error, "Exit:exitGetProperty", wszCRLPROP); // Grab the raw delta CRL (which may not exist) wsprintf(wszCRLPROP, wszPROPRAWDELTACRL L".%u", i); hr = exitGetProperty( pServer, FALSE, // fRequest, wszCRLPROP, PROPTYPE_BINARY, &varDeltaCRL); _PrintIfErrorStr2( hr, "Exit:exitGetProperty", wszCRLPROP, fDeltaCRLsDisabled? HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) : S_OK); if (S_OK != hr && !fDeltaCRLsDisabled) { goto error; } // Publish the CRL(s) ... } hr = S_OK; error: if (NULL != pServer) { pServer->Release(); } VariantClear(&varBaseCRL); VariantClear(&varDeltaCRL); return(hr); } //+-------------------------------------------------------------------------- // CCertExitSample::Notify -- Notify the exit module of an event // // Returns S_OK. //+-------------------------------------------------------------------------- STDMETHODIMP CCertExitSample::Notify( /* [in] */ LONG ExitEvent, /* [in] */ LONG Context) { char *psz = "UNKNOWN EVENT"; HRESULT hr = S_OK; switch (ExitEvent) { case EXITEVENT_CERTISSUED: hr = _NotifyNewCert(Context); psz = "certissued"; break; case EXITEVENT_CERTPENDING: psz = "certpending"; break; case EXITEVENT_CERTDENIED: psz = "certdenied"; break; case EXITEVENT_CERTREVOKED: psz = "certrevoked"; break; case EXITEVENT_CERTRETRIEVEPENDING: psz = "retrievepending"; break; case EXITEVENT_CRLISSUED: hr = _NotifyCRLIssued(Context); psz = "crlissued"; break; case EXITEVENT_SHUTDOWN: psz = "shutdown"; break; } DBGPRINT(( fDebug, "Exit:Notify(%hs=%x, ctx=%x) rc=%x\n", psz, ExitEvent, Context, hr)); return(hr); } STDMETHODIMP CCertExitSample::GetDescription( /* [retval][out] */ BSTR *pstrDescription) { HRESULT hr = S_OK; WCHAR sz[MAX_PATH]; assert(wcslen(wsz_SAMPLE_DESCRIPTION) < ARRAYSIZE(sz)); wcscpy(sz, wsz_SAMPLE_DESCRIPTION); *pstrDescription = SysAllocString(sz); if (NULL == *pstrDescription) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Exit:SysAllocString"); } error: return(hr); } //+-------------------------------------------------------------------------- // CCertExitSample::GetManageModule // // Returns S_OK on success. //+-------------------------------------------------------------------------- STDMETHODIMP CCertExitSample::GetManageModule( /* [out, retval] */ ICertManageModule **ppManageModule) { HRESULT hr; *ppManageModule = NULL; hr = CoCreateInstance( CLSID_CCertManageExitModuleSample, NULL, // pUnkOuter CLSCTX_INPROC_SERVER, IID_ICertManageModule, (VOID **) ppManageModule); _JumpIfError(hr, error, "CoCreateInstance"); error: return(hr); } ///////////////////////////////////////////////////////////////////////////// // STDMETHODIMP CCertExitSample::InterfaceSupportsErrorInfo(REFIID riid) { int i; static const IID *arr[] = { &IID_ICertExit, }; for (i = 0; i < sizeof(arr)/sizeof(arr[0]); i++) { if (IsEqualGUID(*arr[i],riid)) { return(S_OK); } } return(S_FALSE); } HRESULT exitGetProperty( IN ICertServerExit *pServer, IN BOOL fRequest, IN WCHAR const *pwszPropertyName, IN DWORD PropType, OUT VARIANT *pvarOut) { HRESULT hr; BSTR strName = NULL; VariantInit(pvarOut); strName = SysAllocString(pwszPropertyName); if (NULL == strName) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Exit:SysAllocString"); } if (fRequest) { hr = pServer->GetRequestProperty(strName, PropType, pvarOut); _JumpIfErrorStr2( hr, error, "Exit:GetRequestProperty", pwszPropertyName, CERTSRV_E_PROPERTY_EMPTY); } else { hr = pServer->GetCertificateProperty(strName, PropType, pvarOut); _JumpIfErrorStr2( hr, error, "Exit:GetCertificateProperty", pwszPropertyName, CERTSRV_E_PROPERTY_EMPTY); } error: if (NULL != strName) { SysFreeString(strName); } return(hr); }