//+-------------------------------------------------------------------------- // // 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_CERTISSUED | \ EXITEVENT_CERTPENDING | \ EXITEVENT_CERTDENIED | \ EXITEVENT_CERTREVOKED | \ EXITEVENT_CERTRETRIEVEPENDING | \ EXITEVENT_CRLISSUED | \ EXITEVENT_SHUTDOWN #define CERTTYPE_ATTR_NAME TEXT("CertificateTemplate") #define MAX_CRL_PROP (32 + 10) 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; HKEY hkey = NULL; DWORD cbbuf; DWORD dwType; ENUM_CATYPES CAType; LPWSTR pwszCLSIDCertExit = NULL; ICertServerExit* pServer = NULL; VARIANT varValue; WCHAR sz[MAX_PATH]; assert(wcslen(wsz_SAMPLE_DESCRIPTION) < ARRAYSIZE(sz)); wcscpy(sz, wsz_SAMPLE_DESCRIPTION); m_strDescription = SysAllocString(sz); if (NULL == m_strDescription) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "Exit:SysAllocString"); } VariantInit(&varValue); 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 = pServer->GetCertificateProperty(wszPROPMODULEREGLOC, PROPTYPE_STRING, &varValue); _JumpIfErrorStr(hr, error, "Exit:GetCertificateProperty", 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 = pServer->GetCertificateProperty(wszPROPCATYPE, PROPTYPE_LONG, &varValue); _JumpIfErrorStr(hr, error, "Exit:GetCertificateProperty", 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 (ERROR_FILE_NOT_FOUND == hr) { hr = S_OK; goto error; } _JumpError(hr, error, "Exit:RegOpenKeyEx"); } hr = pServer->GetCertificateProperty(wszPROPCERTCOUNT, PROPTYPE_LONG, &varValue); _JumpIfErrorStr(hr, error, "Exit:GetCertificateProperty", 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: if (NULL != hkey) { RegCloseKey(hkey); } 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); } //+-------------------------------------------------------------------------- // 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 wszPath[MAX_PATH]; WCHAR const *pwszFile; WCHAR const *pwsz; hr = pServer->GetRequestAttribute(wszPROPEXITCERTFILE, &strCertFile); if (S_OK != hr) { DBGPRINT(( fDebug, "Exit:GetRequestAttribute(%ws): %x%hs\n", wszPROPEXITCERTFILE, hr, CERTSRV_E_PROPERTY_EMPTY == hr? " EMPTY VALUE" : "")); if (CERTSRV_E_PROPERTY_EMPTY == hr) { hr = S_OK; } goto error; } pwszFile = wcsrchr(strCertFile, L'\\'); if (NULL == pwszFile) { pwszFile = strCertFile; } else { pwszFile++; } pwsz = wcsrchr(pwszFile, L'/'); if (NULL != pwsz) { pwszFile = &pwsz[1]; } hr = _ExpandEnvironmentVariables( L"%SystemRoot%\\System32\\" wszCERTENROLLSHAREPATH L"\\", wszPath, ARRAYSIZE(wszPath)); _JumpIfError(hr, error, "_ExpandEnvironmentVariables"); if (ARRAYSIZE(wszPath) <= wcslen(wszPath) + wcslen(pwszFile)) { hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW); _JumpError(hr, error, "Exit:Path too long"); } wcscat(wszPath, pwszFile); // open file & write binary cert out. hFile = CreateFile( wszPath, 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", wszPath); } if (!WriteFile(hFile, pbCert, cbCert, &cbWritten, NULL)) { hr = ceHLastError(); _JumpErrorStr(hr, error, "Exit:WriteFile", wszPath); } if (cbWritten != cbCert) { hr = STG_E_WRITEFAULT; DBGPRINT(( fDebug, "Exit:WriteFile(%ws): attempted %x, actual %x bytes: %x\n", wszPath, cbCert, cbWritten, hr)); goto error; } error: if (INVALID_HANDLE_VALUE != hFile) { CloseHandle(hFile); } 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; HRESULT hr2; VARIANT varCert; VARIANT varCertType; ICertServerExit *pServer = NULL; BSTR strCertType; VariantInit(&varCert); 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 = pServer->GetCertificateProperty( wszPROPRAWCERTIFICATE, PROPTYPE_BINARY, &varCert); _JumpIfErrorStr( hr, error, "Exit:GetCertificateProperty", wszPROPRAWCERTIFICATE); if (VT_BSTR != varCert.vt) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "Exit:BAD cert var type"); } strCertType = NULL; hr = pServer->GetCertificateProperty( wszPROPCERTIFICATETEMPLATE, PROPTYPE_STRING, &varCertType); if (S_OK == hr && VT_BSTR == varCertType.vt) { strCertType = varCertType.bstrVal; DBGPRINT((fDebug, "Exit:CertType = %ws\n", strCertType)); } hr = S_OK; // only call write fxns if server policy allows if (m_dwExitPublishFlags & EXITPUB_FILE) { hr2 = _WriteCertToFile( pServer, (BYTE const *) varCert.bstrVal, SysStringByteLen(varCert.bstrVal)); _PrintIfError(hr2, "_WriteCertToFile"); hr = hr2; } error: VariantClear(&varCert); VariantClear(&varCertType); 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; DWORD cbbuf; DWORD dwType; 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"); // How many CRLs are there? // Loop for each CRL for (i = 0; i < m_cCACert; i++) { WCHAR wszCRLPROP[MAX_CRL_PROP]; // Verify the CRL State says we should update this CRL wsprintf(wszCRLPROP, wszPROPCRLSTATE L".%u", i); hr = pServer->GetCertificateProperty( wszCRLPROP, PROPTYPE_LONG, &varBaseCRL); _JumpIfErrorStr(hr, error, "Exit:GetCertificateProperty", wszCRLPROP); if (CA_DISP_VALID != varBaseCRL.lVal) { continue; } // Grab the raw base CRL wsprintf(wszCRLPROP, wszPROPRAWCRL L".%u", i); hr = pServer->GetCertificateProperty( wszCRLPROP, PROPTYPE_BINARY, &varBaseCRL); _JumpIfErrorStr(hr, error, "Exit:GetCertificateProperty", wszCRLPROP); // Grab the raw delta CRL (which may not exist) wsprintf(wszCRLPROP, wszPROPRAWDELTACRL L".%u", i); hr = pServer->GetCertificateProperty( wszCRLPROP, PROPTYPE_BINARY, &varDeltaCRL); _PrintIfErrorStr(hr, "Exit:GetCertificateProperty", wszCRLPROP); // Publish the CRL(s) ... } 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); }