Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3219 lines
83 KiB

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File: exit.cpp
//
// Contents: CCertExit implementation
//
//---------------------------------------------------------------------------
#include "pch.cpp"
#pragma hdrstop
#include <ntdsapi.h>
#include <lm.h>
#include <security.h>
#include <dsgetdc.h>
#include <userenv.h>
#include "cainfop.h"
#include "csdisp.h"
#include "cspelog.h"
#include "exitlog.h"
#include "exit.h"
#include "cdosys_i.c"
#include "cdosysstr.h"
#include "cdosys.h"
#include <atlbase.h>
#include <atlimpl.cpp>
#define __dwFILE__ __dwFILE_EXIT_DEFAULT_EXIT_CPP__
// begin_sdksample
#ifndef DBG_CERTSRV
#error -- DBG_CERTSRV not defined!
#endif
#define myEXITEVENTS \
EXITEVENT_CERTISSUED | \
EXITEVENT_CERTPENDING | \
EXITEVENT_CERTDENIED | \
EXITEVENT_CERTREVOKED | \
EXITEVENT_CERTRETRIEVEPENDING | \
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);
}
//+--------------------------------------------------------------------------
// CCertExit::~CCertExit -- destructor
//
// free memory associated with this instance
//+--------------------------------------------------------------------------
CCertExit::~CCertExit()
{
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);
}
}
//+--------------------------------------------------------------------------
// CCertExit::Initialize -- initialize for a CA & return interesting Event Mask
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
STDMETHODIMP
CCertExit::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);
#ifdef IDS_MODULE_NAME // no_sdksample
if (!LoadString(g_hInstance, IDS_MODULE_NAME, sz, ARRAYSIZE(sz))) // no_sdksample
{ // no_sdksample
sz[0] = L'\0'; // no_sdksample
} // no_sdksample
#else // no_sdksample
CSASSERT(wcslen(wsz_SAMPLE_DESCRIPTION) < ARRAYSIZE(sz));
wcsncpy(sz, wsz_SAMPLE_DESCRIPTION, ARRAYSIZE(sz));
sz[ARRAYSIZE(sz) - 1] = L'\0';
#endif // no_sdksample
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 = myEXITEVENTS;
DBGPRINT((DBG_SS_CERTEXIT, "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;
}
// end_sdksample
if (FIsAdvancedServer())
{
hr = m_EmailNotifyObj.Init(m_hExitKey, m_strDescription);
_PrintIfError(hr, "CEmailNotify::Init");
}
// begin_sdksample
hr = S_OK;
error:
VariantClear(&varValue);
if (NULL != pServer)
{
pServer->Release();
}
return(myHError(hr));
}
//+--------------------------------------------------------------------------
// CCertExit::_ExpandEnvironmentVariables -- Expand environment variables
//
//+--------------------------------------------------------------------------
HRESULT
CCertExit::_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 = myHLastError();
_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);
}
//+--------------------------------------------------------------------------
// CCertExit::_WriteCertToFile -- write binary certificate to a file
//
//+--------------------------------------------------------------------------
HRESULT
CCertExit::_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);
// Old functionality asked requester to pass in a CertFile attribute with
// the output request file name. After the security review we decided
// to not allow file name from user but to build it at server. Still, we
// don't want to start publishing all certificates, so we'll maintain the
// CertFile property; if present, we'll just ignore its content, if not
// present we won't publish.
hr = exitGetRequestAttribute(pServer, wszPROPEXITCERTFILE, &strCertFile);
if (S_OK != hr)
{
DBGPRINT((
DBG_SS_CERTEXIT,
"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 = myBuildPathAndExt(wszDir, wszFile, NULL, &pwszPath);
_JumpIfError(hr, error, "myBuildPathAndExt");
// 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 = myHLastError();
_JumpErrorStr(hr, error, "Exit:CreateFile", pwszPath);
}
if (!WriteFile(hFile, pbCert, cbCert, &cbWritten, NULL))
{
hr = myHLastError();
_JumpErrorStr(hr, error, "Exit:WriteFile", pwszPath);
}
if (cbWritten != cbCert)
{
hr = STG_E_WRITEFAULT;
DBGPRINT((
DBG_SS_CERTEXIT,
"Exit:WriteFile(%ws): attempted %x, actual %x bytes: %x\n",
pwszPath,
cbCert,
cbWritten,
hr));
goto error;
}
error:
// end_sdksample
VariantClear(&varRequestID);
if (hr != S_OK)
{
LPCWSTR wszStrings[1];
wszStrings[0] = pwszPath;
::LogModuleStatus(
g_hInstance,
hr,
MSG_UNABLE_TO_WRITEFILE,
FALSE, // fPolicy
m_strDescription,
wszStrings,
NULL);
}
// begin_sdksample
if (INVALID_HANDLE_VALUE != hFile)
{
CloseHandle(hFile);
}
if (NULL != pwszPath)
{
LocalFree(pwszPath);
}
if (NULL != strCertFile)
{
SysFreeString(strCertFile);
}
return(hr);
}
//+--------------------------------------------------------------------------
// CCertExit::_NotifyNewCert -- Notify the exit module of a new certificate
//
//+--------------------------------------------------------------------------
HRESULT
CCertExit::_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);
}
//+--------------------------------------------------------------------------
// CCertExit::_NotifyCRLIssued -- Notify the exit module of a new certificate
//
//+--------------------------------------------------------------------------
HRESULT
CCertExit::_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);
}
//+--------------------------------------------------------------------------
// CCertExit::Notify -- Notify the exit module of an event
//
// Returns S_OK.
//+--------------------------------------------------------------------------
STDMETHODIMP
CCertExit::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:
#if 0 // no_sdksample
hr = _NotifyCRLIssued(Context);
#endif // no_sdksample
psz = "crlissued";
break;
case EXITEVENT_SHUTDOWN:
psz = "shutdown";
break;
}
// end_sdksample
{
HRESULT hr2 = m_EmailNotifyObj.Notify(
ExitEvent,
Context,
m_strDescription);
if(S_OK != hr2)
{
_PrintError(hr2, "Email notification");
if(S_OK == hr)
hr = hr2;
}
}
// begin_sdksample
DBGPRINT((
DBG_SS_CERTEXIT,
"Exit:Notify(%hs=%x, ctx=%x) rc=%x\n",
psz,
ExitEvent,
Context,
hr));
return(hr);
}
STDMETHODIMP
CCertExit::GetDescription(
/* [retval][out] */ BSTR *pstrDescription)
{
HRESULT hr = S_OK;
WCHAR sz[MAX_PATH];
#ifdef IDS_MODULE_NAME // no_sdksample
LoadString(g_hInstance, IDS_MODULE_NAME, sz, ARRAYSIZE(sz));// no_sdksample
#else // no_sdksample
CSASSERT(wcslen(wsz_SAMPLE_DESCRIPTION) < ARRAYSIZE(sz));
wcscpy(sz, wsz_SAMPLE_DESCRIPTION);
#endif // no_sdksample
*pstrDescription = SysAllocString(sz);
if (NULL == *pstrDescription)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "Exit:SysAllocString");
}
error:
return(hr);
}
//+--------------------------------------------------------------------------
// CCertExit::GetManageModule
//
// Returns S_OK on success.
//+--------------------------------------------------------------------------
STDMETHODIMP
CCertExit::GetManageModule(
/* [out, retval] */ ICertManageModule **ppManageModule)
{
HRESULT hr;
*ppManageModule = NULL;
hr = CoCreateInstance(
CLSID_CCertManageExitModule,
NULL, // pUnkOuter
CLSCTX_INPROC_SERVER,
IID_ICertManageModule,
(VOID **) ppManageModule);
_JumpIfError(hr, error, "CoCreateInstance");
error:
return(hr);
}
/////////////////////////////////////////////////////////////////////////////
//
STDMETHODIMP
CCertExit::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);
}
// end_sdksample
/////////////////////////////////////////////////////////////////////////////
HRESULT
exitGetStringProperty(
IN ICertServerExit *pServer,
IN BOOL fRequest,
IN BOOL fAllowUnknown,
OPTIONAL IN WCHAR *pwszProp,
OUT BSTR *pstr)
{
HRESULT hr;
VARIANT var;
WCHAR awc[64];
VariantInit(&var);
CSASSERT(NULL == *pstr);
if (NULL == pwszProp)
{
hr = CERTSRV_E_PROPERTY_EMPTY;
}
else
{
hr = exitGetProperty(
pServer,
fRequest,
pwszProp,
PROPTYPE_STRING,
&var);
}
if (!fAllowUnknown || CERTSRV_E_PROPERTY_EMPTY != hr)
{
_JumpIfErrorStr(hr, error, "Exit:GetProperty", pwszProp);
if (VT_BSTR != var.vt)
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
_JumpErrorStr(hr, error, "Exit:BAD var type", pwszProp);
}
*pstr = var.bstrVal;
var.vt = VT_EMPTY;
}
else
{
if (!LoadString(g_hInstance, IDS_MAPI_UNKNOWN, awc, ARRAYSIZE(awc)))
{
hr = myHLastError();
_JumpError(hr, error, "Exit:LoadString");
}
*pstr = SysAllocString(awc);
if (NULL == *pstr)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "Exit:SysAllocString");
}
}
hr = S_OK;
error:
VariantClear(&var);
return(hr);
}
HRESULT
LoadAnsiResourceString(
IN LONG idmsg,
OUT char **ppszString)
{
HRESULT hr;
WCHAR awc[4096];
if (!LoadString(g_hInstance, idmsg, awc, ARRAYSIZE(awc)))
{
hr = myHLastError();
_JumpError(hr, error, "Exit:LoadString");
}
if (!ConvertWszToSz(ppszString, awc, MAXDWORD))
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "Exit:ConvertWszToSz");
}
hr = S_OK;
error:
return(hr);
}
/////////////////////////////////////////////////////////////////////////////
HRESULT RegGetValue(
HKEY hkey,
LPCWSTR pcwszValName,
VARIANT* pvarValue)
{
HRESULT hr;
DWORD dwType;
DWORD cbVal;
BYTE* pbVal = NULL;
hr = myRegQueryValueEx(
hkey,
pcwszValName,
&dwType,
&pbVal,
&cbVal);
_JumpIfError2(hr, error, "myRegQueryValueEx",
HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
hr = myRegValueToVariant(
dwType,
cbVal,
pbVal,
pvarValue);
_JumpIfError(hr, error, "myRegValueToVariant");
error:
if(pbVal)
{
LocalFree(pbVal);
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
HRESULT RegSetValue(
HKEY hkey,
LPCWSTR pcwszValName,
VARIANT* pvarValue)
{
HRESULT hr;
DWORD dwType;
DWORD cbVal;
BYTE* pbVal = NULL;
hr = myVariantToRegValue(
pvarValue,
&dwType,
&cbVal,
&pbVal);
_JumpIfError(hr, error, "myVariantToRegValue");
hr = RegSetValueEx(
hkey,
pcwszValName,
0,
dwType,
pbVal,
cbVal);
if (S_OK != hr)
{
hr = myHError(hr);
_JumpErrorStr(hr, error, "RegSetValueEx", pcwszValName);
}
error:
if(pbVal)
{
LocalFree(pbVal);
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
HRESULT
GetCertTypeFriendlyName(
IN LPCWSTR pcwszCertType,
OUT LPWSTR *ppwszFriendlyName)
{
HRESULT hr;
CAutoHCERTTYPE hCertType;
WCHAR **apwszNames = NULL;
hr = CAFindCertTypeByName(
pcwszCertType,
NULL, // hCAInfo
CT_FIND_LOCAL_SYSTEM |
CT_ENUM_MACHINE_TYPES |
CT_ENUM_USER_TYPES, // dwFlags
&hCertType);
if(HRESULT_FROM_WIN32(ERROR_NOT_FOUND) == hr)
{
// try with the OID
hr = CAFindCertTypeByName(
pcwszCertType,
NULL,
CT_FIND_LOCAL_SYSTEM |
CT_ENUM_MACHINE_TYPES |
CT_ENUM_USER_TYPES |
CT_FIND_BY_OID,
&hCertType);
}
_JumpIfErrorStr(hr, error, "Exit:CAFindCertTypeByName", pcwszCertType);
hr = CAGetCertTypeProperty(
hCertType,
CERTTYPE_PROP_FRIENDLY_NAME,
&apwszNames);
_JumpIfError(hr, error, "Exit:CAGetCertTypeProperty");
if (NULL == apwszNames[0])
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
_JumpError(hr, error, "Exit:NULL friendly name");
}
*ppwszFriendlyName = (LPWSTR) LocalAlloc(LMEM_FIXED,
sizeof(WCHAR)*(wcslen(apwszNames[0])+1));
_JumpIfAllocFailed(*ppwszFriendlyName, error);
wcscpy(*ppwszFriendlyName, apwszNames[0]);
error:
if (NULL != apwszNames)
{
CAFreeCertTypeProperty(hCertType, apwszNames);
}
return(hr);
}
/////////////////////////////////////////////////////////////////////////////
// Email notification support
LPCWSTR CEmailNotify::m_pcwszEventRegKeys[CEmailNotify::m_gcEvents] =
{
wszREGEXITISSUEDKEY,
wszREGEXITPENDINGKEY,
wszREGEXITDENIEDKEY,
wszREGEXITREVOKEDKEY,
wszREGEXITCRLISSUEDKEY,
wszREGEXITSHUTDOWNKEY,
wszREGEXITSTARTUPKEY,
};
/////////////////////////////////////////////////////////////////////////////
CEmailNotify::CEmailNotify()
{
m_hkeySMTP = NULL;
m_dwEventFilter = 0;
m_bstrCAMailAddress = NULL;
m_pICDOConfig = NULL;
m_fReloadCDOConfig = true;
VariantInit(&m_varTemplateRestrictions);
}
/////////////////////////////////////////////////////////////////////////////
CEmailNotify::~CEmailNotify()
{
if (m_pICDOConfig)
{
m_pICDOConfig->Release();
}
VariantClear(&m_varTemplateRestrictions);
if(m_bstrCAMailAddress)
{
SysFreeString(m_bstrCAMailAddress);
}
if(m_hkeySMTP)
{
RegCloseKey(m_hkeySMTP);
}
}
/////////////////////////////////////////////////////////////////////////////
HRESULT
CEmailNotify::Init(
IN HKEY hkeyExit,
IN WCHAR const *pwszDescription)
{
HRESULT hr;
VARIANT varValue;
hr = RegOpenKeyEx(
hkeyExit,
wszREGEXITSMTPKEY,
0, // dwReserved
KEY_READ | KEY_QUERY_VALUE,
&m_hkeySMTP);
if ((HRESULT) ERROR_FILE_NOT_FOUND == hr || S_OK == hr)
{
hr = _CreateSMTPRegSettings(hkeyExit);
_JumpIfError(hr, error, "CreateSMTPRegSettings");
}
_JumpIfError(hr, error, "RegOpenKey(SMTP)");
// load event filter
hr = RegGetValue(
m_hkeySMTP,
wszREGEXITSMTPEVENTFILTER,
&varValue);
if(S_OK==hr)
{
m_dwEventFilter = V_I4(&varValue);
}
else if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr)
{
hr = S_OK;
}
_JumpIfErrorStr(hr, error, "RegGetValue", wszREGEXITSMTPEVENTFILTER);
m_dwEventFilter = V_I4(&varValue);
if(m_dwEventFilter) // no need to load config if no notification enabled
{
// load per event type info from each subkey
hr = _LoadEventInfoFromRegistry();
_JumpIfError(hr, error, "_LoadEventInfoFromRegistry");
// load template restrictions
hr = _LoadTemplateRestrictionsFromRegistry();
_JumpIfError(hr, error, "CCertExit::_LoadTemplateRestrictionsFromRegistry");
hr = _InitCDO();
_JumpIfError(hr, error, "CEmailNotify::InitCDO");
// send startup notification mail
hr = Notify(EXITEVENT_STARTUP, 0, pwszDescription);
_PrintIfError(hr, "Notify(EXITEVENT_STARTUP)");
}
hr = S_OK;
error:
if (S_OK != hr && m_pICDOConfig)
{
m_pICDOConfig->Release();
m_pICDOConfig = NULL;
}
return(hr);
}
/////////////////////////////////////////////////////////////////////////////
HRESULT CEmailNotify::_InitCDO()
{
HRESULT hr;
Fields* pFields = NULL;
IConfiguration *pICDOConfig = NULL;
// load SMTP fields
hr = CoCreateInstance(CDO::CLSID_Configuration,
NULL,
CLSCTX_INPROC_SERVER,
CDO::IID_IConfiguration,
reinterpret_cast<void**>(&pICDOConfig));
_JumpIfError(hr, error, "CoCreateInstance CDO_IConfiguration");
hr = pICDOConfig->get_Fields(&pFields);
_JumpIfError(hr, error, "CDO::IConfig::get_Fields");
hr = _LoadSMTPFieldsFromRegistry(pFields);
_JumpIfError(hr, error, "_LoadFieldsFromRegistry");
hr = _LoadSMTPFieldsFromLSASecret(pFields);
// don't bail, optional fields
_PrintIfError(hr, "_LoadFieldsFromLSASecret");
hr = pFields->Update();
_JumpIfError(hr, error, "config");
m_rwlockCDOConfig.GetExclusive();
if(m_pICDOConfig)
{
m_pICDOConfig->Release();
}
m_pICDOConfig = pICDOConfig;
pICDOConfig = NULL;
m_fReloadCDOConfig = false;
m_rwlockCDOConfig.Release();
error:
if(pFields)
{
pFields->Release();
}
if(pICDOConfig)
{
pICDOConfig->Release();
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
inline bool CEmailNotify::_IsEventEnabled(DWORD dwEvent)
{
return (dwEvent & m_dwEventFilter)?true:false;
}
/////////////////////////////////////////////////////////////////////////////
//
// Create the following registry structure under exit key
//
// SMTP
// EventFilter DWORD
// SMTPServer SZ
// SMTPAuthenticate DWORD
//
// Issued
// BodyFormat SZ
// BodyArg MULTISZ
// TitleFormat SZ
// TitleArg MULTISZ
//
// Pending
// ...same as Issued
// Denied
// ...same as Issued
// Revoked
// ...same as Issued
// CLRIssued
// ...same as Issued
// Shutdown
// ...same as Issued
//
HRESULT CEmailNotify::_CreateSMTPRegSettings(HKEY hkeyExit)
{
HRESULT hr;
DWORD dwDisp;
VARIANT varValue;
HKEY hkeyEvent = NULL;
typedef struct tagEventFormat
{
LPCWSTR pcwszRegKey;
int nTitleFormatResourceID;
int nBodyFormatResourceID;
LPWSTR pcwszBodyArg;
DWORD cbBodyArg;
LPWSTR pcwszTitleArg;
DWORD cbTitleArg;
} EventFormat;
WCHAR wszzTitleArg[] =
wszPROPSANITIZEDCANAME
L"\0";
WCHAR wszzBodyArgIssued[] =
wszPROPCERTIFICATEREQUESTID L"\0"
wszPROPUPN L"\0"
wszPROPREQUESTDOT wszPROPREQUESTERNAME L"\0"
wszPROPCERTIFICATESERIALNUMBER L"\0"
wszPROPCERTIFICATENOTBEFOREDATE L"\0"
wszPROPCERTIFICATENOTAFTERDATE L"\0"
wszPROPDISTINGUISHEDNAME L"\0"
wszPROPCERTTEMPLATE L"\0"
wszPROPCERTIFICATEHASH L"\0"
wszPROPREQUESTDOT wszPROPREQUESTDISPOSITIONMESSAGE L"\0"
;
WCHAR wszzBodyArgPending[] =
wszPROPREQUESTDOT wszPROPREQUESTREQUESTID L"\0"
wszPROPUPN L"\0"
wszPROPREQUESTDOT wszPROPREQUESTERNAME L"\0"
wszPROPREQUESTDOT wszPROPREQUESTSUBMITTEDWHEN L"\0"
wszPROPREQUESTDOT wszPROPDISTINGUISHEDNAME L"\0"
wszPROPCERTTEMPLATE L"\0"
wszPROPREQUESTDOT wszPROPREQUESTDISPOSITIONMESSAGE L"\0"
;
WCHAR wszzBodyArgDenied[] =
wszPROPREQUESTDOT wszPROPREQUESTREQUESTID L"\0"
wszPROPREQUESTDOT wszPROPREQUESTERNAME L"\0"
wszPROPREQUESTDOT wszPROPREQUESTSUBMITTEDWHEN L"\0"
wszPROPREQUESTDOT wszPROPDISTINGUISHEDNAME L"\0"
wszPROPREQUESTDOT wszPROPREQUESTDISPOSITIONMESSAGE L"\0"
wszPROPREQUESTDOT wszPROPREQUESTSTATUSCODE L"\0"
;
WCHAR wszzBodyArgRevoked[] =
wszPROPCERTIFICATEREQUESTID L"\0"
wszPROPREQUESTDOT wszPROPREQUESTREVOKEDWHEN L"\0"
wszPROPREQUESTDOT wszPROPREQUESTREVOKEDEFFECTIVEWHEN L"\0"
wszPROPREQUESTDOT wszPROPREQUESTREVOKEDREASON L"\0"
wszPROPUPN L"\0"
wszPROPREQUESTDOT wszPROPREQUESTERNAME L"\0"
wszPROPCERTIFICATESERIALNUMBER L"\0"
wszPROPCERTIFICATENOTBEFOREDATE L"\0"
wszPROPCERTIFICATENOTAFTERDATE L"\0"
wszPROPDISTINGUISHEDNAME L"\0"
wszPROPCERTTEMPLATE L"\0"
wszPROPCERTIFICATEHASH L"\0"
wszPROPREQUESTDOT wszPROPREQUESTSTATUSCODE L"\0"
;
WCHAR wszzBodyArgCRLIssued[] = L"\0";
WCHAR wszzBodyArgShutdown[] = L"\0";
WCHAR wszzBodyArgStartup[] = L"\0";
// order in this list must match the order of events in m_pcwszEventRegKeys
EventFormat FormatList[] =
{
// issued
{
wszREGEXITISSUEDKEY,
IDS_TITLEFORMAT_ISSUED,
IDS_BODYFORMAT_ISSUED,
wszzBodyArgIssued,
sizeof(wszzBodyArgIssued),
wszzTitleArg,
sizeof(wszzTitleArg),
},
// pending
{
wszREGEXITPENDINGKEY,
IDS_TITLEFORMAT_PENDING,
IDS_BODYFORMAT_PENDING,
wszzBodyArgPending,
sizeof(wszzBodyArgPending),
wszzTitleArg,
sizeof(wszzTitleArg),
},
// denied
{
wszREGEXITDENIEDKEY,
IDS_TITLEFORMAT_DENIED,
IDS_BODYFORMAT_DENIED,
wszzBodyArgDenied,
sizeof(wszzBodyArgDenied),
wszzTitleArg,
sizeof(wszzTitleArg),
},
// revoked
{
wszREGEXITREVOKEDKEY,
IDS_TITLEFORMAT_REVOKED,
IDS_BODYFORMAT_REVOKED,
wszzBodyArgRevoked,
sizeof(wszzBodyArgRevoked),
wszzTitleArg,
sizeof(wszzTitleArg),
},
// CRL issued
{
wszREGEXITCRLISSUEDKEY,
IDS_TITLEFORMAT_CRLISSUED,
IDS_BODYFORMAT_CRLISSUED,
wszzBodyArgCRLIssued,
sizeof(wszzBodyArgCRLIssued),
wszzTitleArg,
sizeof(wszzTitleArg),
},
// shutdown
{
wszREGEXITSHUTDOWNKEY,
IDS_TITLEFORMAT_SHUTDOWN,
IDS_BODYFORMAT_SHUTDOWN,
wszzBodyArgShutdown,
sizeof(wszzBodyArgShutdown),
wszzTitleArg,
sizeof(wszzTitleArg),
},
// startup
{
wszREGEXITSTARTUPKEY,
IDS_TITLEFORMAT_STARTUP,
IDS_BODYFORMAT_STARTUP,
wszzBodyArgStartup,
sizeof(wszzBodyArgStartup),
wszzTitleArg,
sizeof(wszzTitleArg),
},
};
if(!m_hkeySMTP)
{
hr = RegCreateKeyEx(
hkeyExit,
wszREGEXITSMTPKEY,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&m_hkeySMTP,
&dwDisp);
_JumpIfErrorStr(hr, error, "RegCreateKeyEx", wszREGEXITSMTPKEY);
if(REG_CREATED_NEW_KEY==dwDisp)
{
V_VT(&varValue) = VT_I4;
V_I4(&varValue) = 0; // all notifications disabled
hr = RegSetValue(
m_hkeySMTP,
wszREGEXITSMTPEVENTFILTER,
&varValue);
_JumpIfErrorStr(hr, error, "RegSetValue", wszREGEXITSMTPEVENTFILTER);
VariantClear(&varValue);
V_VT(&varValue) = VT_BSTR;
V_BSTR(&varValue) = SysAllocString(L"");// just create the value, user
hr = RegSetValue( // needs to set server name
m_hkeySMTP,
wszREGEXITSMTPSERVER,
&varValue);
_JumpIfErrorStr(hr, error, "RegSetValue", wszREGEXITSMTPSERVER);
VariantClear(&varValue);
V_VT(&varValue) = VT_I4;
V_I4(&varValue) = cdoAnonymous;
hr = RegSetValue(
m_hkeySMTP,
wszREGEXITSMTPAUTHENTICATE,
&varValue);
_JumpIfErrorStr(hr, error, "RegSetValue", wszREGEXITSMTPAUTHENTICATE);
VariantClear(&varValue);
}
}
for(int i=0; i<ARRAYSIZE(FormatList); i++)
{
CAutoLPWSTR pwszBodyFormat;
CAutoLPWSTR pwszTitleFormat;
// create key
hr = RegCreateKeyEx(
m_hkeySMTP,
FormatList[i].pcwszRegKey,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hkeyEvent,
&dwDisp);
_JumpIfErrorStr(hr, error, "RegCreateKeyEx", wszREGEXITSMTPKEY);
if(REG_CREATED_NEW_KEY==dwDisp)
{
// set body format
hr = myLoadRCString(
g_hInstance,
FormatList[i].nBodyFormatResourceID,
&pwszBodyFormat);
_JumpIfError(hr, error, "myLoadRCString body format");
// Body format is a multi line string, convert it to multisz
// in place by replacing \n with \0
// Also if we find consecutive \n's, insert spaces in between
// so we don't end up with a double \0 which normally marks
// the end of multisz.
{
DWORD cbBodyFormat = sizeof(WCHAR)*(wcslen(pwszBodyFormat)+1);
CAutoLPWSTR pwszBodyFormatFixed = (LPWSTR)LocalAlloc(
LMEM_FIXED|LMEM_ZEROINIT,
cbBodyFormat*2); // worst case scenario buffer has only \n's
_JumpIfAllocFailed(pwszBodyFormatFixed, error);
WCHAR *pchSrc, *pchDest;
for(cbBodyFormat=2, pchSrc=pwszBodyFormat, pchDest=pwszBodyFormatFixed;
*pchSrc;
pchSrc++, pchDest++, cbBodyFormat++)
{
if(L'\n' == *pchSrc)
{
*pchDest = L'\0';
if(L'\n' == *(pchSrc+1)) // detected \n\n, insert space in between
{
*++pchDest = L' ';
cbBodyFormat++;
}
} else
{
*pchDest = *pchSrc;
}
}
*pchDest++ = L'\0';
*pchDest = L'\0';
cbBodyFormat *= 2;
hr = RegSetValueEx(
hkeyEvent,
wszREGEXITBODYFORMAT,
0,
REG_MULTI_SZ,
(CONST BYTE*) (LPCWSTR) pwszBodyFormatFixed,
cbBodyFormat);
_JumpIfErrorStr(hr, error, "RegSetValueEx", wszREGEXITBODYFORMAT);
}
// set body args
hr = RegSetValueEx(
hkeyEvent,
wszREGEXITBODYARG,
0,
REG_MULTI_SZ,
(CONST BYTE*) FormatList[i].pcwszBodyArg,
FormatList[i].cbBodyArg);
_JumpIfErrorStr(hr, error, "RegSetValueEx", wszREGEXITBODYARG);
// set title format
hr = myLoadRCString(
g_hInstance,
FormatList[i].nTitleFormatResourceID,
&pwszTitleFormat);
_JumpIfError(hr, error, "myLoadRCString title format");
hr = RegSetValueEx(
hkeyEvent,
wszREGEXITTITLEFORMAT,
0,
REG_SZ,
(CONST BYTE*) (LPCWSTR) pwszTitleFormat,
sizeof(WCHAR)*(wcslen(pwszTitleFormat)+1));
_JumpIfErrorStr(hr, error, "RegSetValueEx", wszREGEXITTITLEFORMAT);
// set title args
hr = RegSetValueEx(
hkeyEvent,
wszREGEXITTITLEARG,
0,
REG_MULTI_SZ,
(CONST BYTE*) FormatList[i].pcwszTitleArg,
FormatList[i].cbTitleArg);
_JumpIfErrorStr(hr, error, "RegSetValueEx", wszREGEXITBODYARG);
RegCloseKey(hkeyEvent);
hkeyEvent = NULL;
}
}
hr = S_OK;
error:
if(hkeyEvent)
{
RegCloseKey(hkeyEvent);
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
HRESULT CEmailNotify::_LoadTemplateRestrictionsFromRegistry()
{
HRESULT hr;
hr = RegGetValue(
m_hkeySMTP,
wszREGEXITSMTPTEMPLATES,
&m_varTemplateRestrictions);
if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr)
{
hr =S_OK;
}
_JumpIfErrorStr(hr, error, "RegGetValue", wszREGEXITSMTPTEMPLATES);
error:
return hr;
}
/////////////////////////////////////////////////////////////////////////////
inline bool CEmailNotify::_TemplateRestrictionsEnabled(DWORD dwEvent)
{
if((dwEvent & EXITEVENT_CRLISSUED) ||
(dwEvent & EXITEVENT_SHUTDOWN) ||
(dwEvent & EXITEVENT_STARTUP) ||
VT_EMPTY==V_VT(&m_varTemplateRestrictions))
{
return false;
}
else
{
return true;
}
}
/////////////////////////////////////////////////////////////////////////////
bool CEmailNotify::_IsRestrictedTemplate(BSTR strTemplate)
{
// attempt to retrieve the name and OID for this template
CAutoHCERTTYPE hCertType;
bool fFoundByName = true;
CAutoBSTR strAlternateTemplateName;
if(!strTemplate)
return true;
HRESULT hr = CAFindCertTypeByName(
strTemplate,
NULL,
CT_FIND_LOCAL_SYSTEM |
CT_ENUM_MACHINE_TYPES |
CT_ENUM_USER_TYPES,
&hCertType);
if(HRESULT_FROM_WIN32(ERROR_NOT_FOUND) == hr)
{
// try with the OID
hr = CAFindCertTypeByName(
strTemplate,
NULL,
CT_FIND_LOCAL_SYSTEM |
CT_ENUM_MACHINE_TYPES |
CT_ENUM_USER_TYPES |
CT_FIND_BY_OID,
&hCertType);
fFoundByName = false;
}
if(S_OK==hr)
{
LPWSTR *ppwszProp = NULL;
hr = CAGetCertTypeProperty(
hCertType,
fFoundByName?CERTTYPE_PROP_OID:CERTTYPE_PROP_CN,
&ppwszProp);
if(S_OK==hr)
{
if(ppwszProp && ppwszProp[0])
{
strAlternateTemplateName = SysAllocString(
ppwszProp[0]);
}
CAFreeCertTypeProperty(
hCertType,
ppwszProp);
}
}
// Must be an array of BSTRs, otherwise loading from registry
// should have failed
CSASSERT((VT_ARRAY|VT_BSTR)==V_VT(&m_varTemplateRestrictions));
BSTR strTempl;
SafeArrayEnum<BSTR> saenumTemplates(V_ARRAY(&m_varTemplateRestrictions));
while(S_OK==saenumTemplates.Next(strTempl))
{
if((strTemplate &&
0==mylstrcmpiL(strTempl, strTemplate)) ||
(strAlternateTemplateName &&
0==mylstrcmpiL(strTempl, strAlternateTemplateName)))
{
// found it, send mail
return false;
}
}
// template not in the list, don't send mail
return true;
}
/////////////////////////////////////////////////////////////////////////////
HRESULT CEmailNotify::_LoadSMTPFieldsFromRegistry(Fields* pFields)
{
HRESULT hr;
DWORD dwType;
BYTE* pbValue = NULL;
DWORD cbValue;
VARIANT varValue;
DWORD dwIndex;
LPWSTR pwszValName = NULL;
DWORD cbValName;
DWORD cValues;
static LPCWSTR pcwszHTTP = L"http://";
static size_t cHTTP = wcslen(pcwszHTTP);
// load cdoSMTPServer
hr = RegGetValue(
m_hkeySMTP,
wszREGEXITSMTPSERVER,
&varValue);
_JumpIfErrorStr(hr, error, "error retrieving SMTP field", wszREGEXITSMTPSERVER);
if(V_VT(&varValue) != VT_BSTR)
{
VariantClear(&varValue);
hr = HRESULT_FROM_WIN32(ERROR_BADKEY);
_JumpErrorStr(hr, error, "invalid field", wszREGEXITSMTPSERVER);
}
hr = _SetField(
pFields,
cdoSMTPServer,
&varValue);
_JumpIfErrorStr(hr, error, "_SetField", cdoSMTPServer);
VariantClear(&varValue);
// authentication method optional
hr = RegGetValue(
m_hkeySMTP,
wszREGEXITSMTPAUTHENTICATE,
&varValue);
if(S_OK==hr &&
V_VT(&varValue) != VT_I4)
{
hr = HRESULT_FROM_WIN32(ERROR_BADKEY);
_JumpErrorStr(hr, error, "invalid CC field", wszREGEXITSMTPAUTHENTICATE);
}
if(S_OK == hr)
{
hr = _SetField(
pFields,
cdoSMTPAuthenticate,
&varValue);
_JumpIfErrorStr(hr, error, "_SetField", cdoSMTPAuthenticate);
VariantClear(&varValue);
}
else if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr)
{
hr = S_OK;
}
else
{
_JumpErrorStr(hr, error, "RegGetValue", wszREGEXITSMTPAUTHENTICATE);
}
// set other SMTP defaults
V_VT(&varValue) = VT_I4;
V_I4(&varValue) = 25; // well known SMTP port
hr = _SetField(
pFields,
cdoSMTPServerPort,
&varValue);
_JumpIfErrorStr(hr, error, "_SetField", cdoSMTPAuthenticate);
V_VT(&varValue) = VT_I4;
V_I4(&varValue) = cdoSendUsingPort;
hr = _SetField(
pFields,
cdoSendUsingMethod,
&varValue);
_JumpIfErrorStr(hr, error, "_SetField", cdoSendUsingMethod);
// enumerate and set any other CDO fields (only if value name is full HTTP URL)
hr = RegQueryInfoKey(
m_hkeySMTP,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&cValues,
&cbValName,
&cbValue,
NULL,
NULL);
_JumpIfErrorStr(hr, error, "RegQueryInfoKey", wszREGEXITSMTPKEY);
pwszValName = (LPWSTR) LocalAlloc(LMEM_FIXED, ++cbValName*sizeof(WCHAR));
_JumpIfAllocFailed(pwszValName, error);
pbValue = (BYTE *) LocalAlloc(LMEM_FIXED, cbValue);
_JumpIfAllocFailed(pbValue, error);
for(dwIndex=0;dwIndex<cValues;dwIndex++)
{
DWORD cbValueTemp = cbValue;
DWORD cbValNameTemp = cbValName;
hr = RegEnumValue(
m_hkeySMTP,
dwIndex,
pwszValName,
&cbValNameTemp,
NULL,
&dwType,
pbValue,
&cbValueTemp);
if (hr != S_OK)
{
hr = myHError(hr);
_JumpError(hr, error, "RegEnumValue");
}
// ignore if not an HTTP URL
if(_wcsnicmp(pwszValName, pcwszHTTP, cHTTP))
{
continue;
}
hr = myRegValueToVariant(
dwType,
cbValueTemp,
pbValue,
&varValue);
_JumpIfError(hr, error, "myRegValueToVariant");
hr = _SetField(
pFields,
pwszValName,
&varValue);
_JumpIfError(hr, error, "_SetField");
VariantClear(&varValue);
}
hr = S_OK;
error:
VariantClear(&varValue);
if(pwszValName)
{
LocalFree(pwszValName);
}
if(pbValue)
{
LocalFree(pbValue);
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
HRESULT CEmailNotify::_SetField(
Fields* pFields,
LPCWSTR pcwszFieldSchemaName,
VARIANT *pvarFieldValue)
{
HRESULT hr;
Field* pfld = NULL;
VARIANT vtName;
VariantInit(&vtName);
V_VT(&vtName) = VT_BSTR;
V_BSTR(&vtName) = SysAllocString(pcwszFieldSchemaName);
_JumpIfAllocFailed(V_BSTR(&vtName), error);
hr = pFields->get_Item(vtName, &pfld);
_JumpIfErrorStr(hr, error, "CDO::Field::get_Item", pcwszFieldSchemaName);
hr = pfld->put_Value(*pvarFieldValue);
_JumpIfErrorStr(hr, error, "CDO::Field::put_Value", pcwszFieldSchemaName);
error:
VariantClear(&vtName);
if(pfld)
{
pfld->Release();
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
HRESULT CEmailNotify::_LoadSMTPFieldsFromLSASecret(
Fields* pFields)
{
HRESULT hr;
VARIANT var; // don't clear
LPWSTR pwszProfileName = NULL;
LPWSTR pwszLogonName = NULL;
LPWSTR pwszPassword = NULL;
BSTR bstrLogonName = NULL;
BSTR bstrPassword = NULL;
hr = myGetMapiInfo(
NULL,
&pwszProfileName, // not used
&pwszLogonName,
&pwszPassword);
if(S_OK == hr) // if NTLM is used, username & password aren't needed
{
bstrLogonName = SysAllocString(pwszLogonName);
_JumpIfAllocFailed(bstrLogonName, error);
bstrPassword = SysAllocString(pwszPassword);
_JumpIfAllocFailed(bstrPassword, error);
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = bstrLogonName;
hr = _SetField(
pFields,
cdoSendUserName,
&var);
_JumpIfError(hr, error, "_SetField");
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = bstrPassword;
hr = _SetField(
pFields,
cdoSendPassword,
&var);
_JumpIfError(hr, error, "_SetField");
}
hr = S_OK;
error:
if (NULL != pwszProfileName)
{
LocalFree(pwszProfileName);
}
if (NULL != pwszLogonName)
{
LocalFree(pwszLogonName);
}
if (NULL != pwszPassword)
{
myZeroDataString(pwszPassword); // password data
LocalFree(pwszPassword);
}
if(NULL != bstrLogonName)
{
SysFreeString(bstrLogonName);
}
if(NULL != bstrPassword)
{
myZeroDataString(bstrPassword); // password data
SysFreeString(bstrPassword);
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
HRESULT CEmailNotify::_LoadEventInfoFromRegistry()
{
HRESULT hr;
CSASSERT(m_hkeySMTP);
for(int i=0; i<m_gcEvents;i++)
{
hr = m_NotifyInfoArray[i].LoadInfoFromRegistry(
m_hkeySMTP,
m_pcwszEventRegKeys[i]);
_JumpIfErrorStr(hr, error,
"CNotifyInfo::LoadInfoFromRegistry",
m_pcwszEventRegKeys[i]);
}
hr = S_OK;
error:
return hr;
}
/////////////////////////////////////////////////////////////////////////////
HRESULT CEmailNotify::_GetCAMailAddress(
ICertServerExit* pServer,
BSTR& rbstrAddress)
{
HRESULT hr;
VARIANT varMachineDNSName;
VARIANT varCAName;
CAutoLPWSTR pwszMailAddr;
VariantInit(&varCAName);
VariantInit(&varMachineDNSName);
if(!m_bstrCAMailAddress)
{
//
// CA_name@machine_dns_name
//
hr = exitGetProperty(
pServer,
FALSE, // fRequest
wszPROPSANITIZEDCANAME,
PROPTYPE_STRING,
&varCAName);
_JumpIfErrorStr(hr, error, "Exit:GetCertificateProperty",
wszPROPSANITIZEDCANAME);
hr = exitGetProperty(
pServer,
FALSE, // fRequest
wszPROPMACHINEDNSNAME,
PROPTYPE_STRING,
&varMachineDNSName);
_JumpIfErrorStr(hr, error, "Exit:GetCertificateProperty",
wszPROPMACHINEDNSNAME);
pwszMailAddr = (LPWSTR) LocalAlloc(
LMEM_FIXED,
sizeof(WCHAR)*(SysStringLen(V_BSTR(&varCAName))+
SysStringLen(V_BSTR(&varMachineDNSName))
+2));
_JumpIfAllocFailed(pwszMailAddr, error);
wcscpy(pwszMailAddr, V_BSTR(&varCAName));
wcscat(pwszMailAddr, L"@");
wcscat(pwszMailAddr, V_BSTR(&varMachineDNSName));
m_bstrCAMailAddress = SysAllocString(pwszMailAddr);
_JumpIfAllocFailed(m_bstrCAMailAddress, error);
}
rbstrAddress = m_bstrCAMailAddress;
hr = S_OK;
error:
VariantClear(&varCAName);
VariantClear(&varMachineDNSName);
return(hr);
}
/////////////////////////////////////////////////////////////////////////////
HRESULT CEmailNotify::_GetEmailFromCertSubject(
const VARIANT *pVarCert,
BSTR *pbstrEmail)
{
HRESULT hr;
PCCERT_CONTEXT pcc = NULL;
CAutoLPWSTR strSubjectEmail;
pcc = CertCreateCertificateContext(
X509_ASN_ENCODING,
(const BYTE *)V_BSTR(pVarCert),
SysStringByteLen(V_BSTR(pVarCert)));
if(!pcc)
{
hr = myHLastError();
_JumpError(hr, error, "CertCreateCertificateContext");
}
hr = myGetCertSubjectField(
pcc,
szOID_RSA_emailAddr,
&strSubjectEmail);
_JumpIfError(hr, error, "myGetCertSubjectField");
*pbstrEmail = SysAllocString(strSubjectEmail);
_JumpIfAllocFailed(*pbstrEmail, error);
error:
if(pcc)
{
CertFreeCertificateContext(pcc);
}
return hr;
}
inline DWORD CEmailNotify::_MapEventToOrd(LONG lEvent)
{
switch (lEvent)
{ case EXITEVENT_CERTISSUED: return 0;
case EXITEVENT_CERTPENDING: return 1;
case EXITEVENT_CERTDENIED: return 2;
case EXITEVENT_CERTREVOKED: return 3;
// not impl case EXITEVENT_CERTRETRIEVEPENDING:
case EXITEVENT_CRLISSUED: return 4;
case EXITEVENT_SHUTDOWN: return 5;
case EXITEVENT_STARTUP: return 6;
default: return MAXDWORD;
}
}
/////////////////////////////////////////////////////////////////////////////
HRESULT
CEmailNotify::Notify(
IN DWORD lExitEvent,
IN LONG lContext,
IN WCHAR const *pwszDescription)
{
HRESULT hr;
ICertServerExit* pServer = NULL;
IMessage* pMsg = NULL;
IBodyPart *pBp = NULL;
CNotifyInfo *pNotifyInfo;
BSTR bstrCharSet = NULL;
BSTR bstrSetTo, bstrSetFrom; // no free
BSTR bstrTo = NULL;
BSTR bstrTitle = NULL;
BSTR bstrBody = NULL;
BSTR bstrTemplate = NULL;
VARIANT varCert;
bool fRWLockAcquired = false;
bool fRetryReloadCDOConfig = false;
VariantInit(&varCert);
bstrSetTo = NULL;
if(_IsEventEnabled(lExitEvent))
{
hr = GetServerCallbackInterface(&pServer, lContext);
_JumpIfError(hr, error, "Exit:GetServerCallbackInterface");
if(_TemplateRestrictionsEnabled(lExitEvent))
{
hr = exitGetStringProperty(
pServer,
FALSE,
FALSE,
wszPROPCERTIFICATETEMPLATE,
&bstrTemplate);
if((S_OK == hr || CERTSRV_E_PROPERTY_EMPTY == hr) &&
_IsRestrictedTemplate(bstrTemplate))
{
// don't send mail for this template
hr = S_OK;
goto error;
}
_JumpIfErrorStr(hr, error, "exitGetStringProperty",
wszPROPCERTIFICATETEMPLATE);
}
pNotifyInfo = &m_NotifyInfoArray[_MapEventToOrd(lExitEvent)];
hr = CoCreateInstance(CDO::CLSID_Message,
NULL,
CLSCTX_INPROC_SERVER,
CDO::IID_IMessage,
reinterpret_cast<void**>(&pMsg));
_JumpIfError(hr, error, "CoCreateInstance CDO_IConfiguration");
///////////////////////////////////////////////////////////////////////////
// Set recipient. Override with registry To field if found.
if(VT_BSTR == V_VT(&pNotifyInfo->m_varTo))
{
bstrSetTo = V_BSTR(&pNotifyInfo->m_varTo);
}
else
{
// Recipient not enforced in the registry, try to find one in the cert.
// For CRL and shutdown events, no cert is available, so To field is
// mandatory
if(lExitEvent&EXITEVENT_CRLISSUED ||
lExitEvent&EXITEVENT_SHUTDOWN ||
lExitEvent&EXITEVENT_STARTUP)
{
hr = HRESULT_FROM_WIN32(ERROR_BADKEY);
_JumpError(hr, error,
"Recipient field is mandatory for CRL or SHUTDOWN events");
}
// look for recipient's address in email cert property (retrieved from
// subject alt name)
hr = exitGetStringProperty(
pServer,
FALSE,
FALSE,
wszPROPEMAIL,
&bstrTo);
// no email in subject alt name, try in subject
if(CERTSRV_E_PROPERTY_EMPTY == hr)
{
hr = exitGetProperty(
pServer,
FALSE, // fRequest
wszPROPRAWCERTIFICATE,
PROPTYPE_BINARY,
&varCert);
if(S_OK == hr)
{
hr = _GetEmailFromCertSubject(
&varCert,
&bstrTo);
}
}
// no email in subject, send to CC list if any
if(S_OK != hr && VT_BSTR == V_VT(&pNotifyInfo->m_varCC))
{
hr = S_OK;
bstrSetTo = V_BSTR(&pNotifyInfo->m_varCC);
}
_JumpIfError(hr, error, "failed to find a recipient");
if(bstrTo)
{
bstrSetTo = bstrTo;
}
}
hr = pMsg->put_To(bstrSetTo);
_JumpIfError(hr, error, "put_To");
///////////////////////////////////////////////////////////////////////
// Set sender. If not specified in the registry, build it:
//
// CAName@MachineDNSName
//
if(VT_BSTR == V_VT(&pNotifyInfo->m_varFrom))
{
bstrSetFrom = V_BSTR(&pNotifyInfo->m_varFrom);
}
else
{
hr = _GetCAMailAddress(pServer, bstrSetFrom);
_JumpIfError(hr, error, "CEmailNotify::_GetCAMailAddress");
}
hr = pMsg->put_From(bstrSetFrom);
_JumpIfError(hr, error, "put_From");
///////////////////////////////////////////////////////////////////////////
// Set CC list, if set in registry
if(VT_BSTR==V_VT(&pNotifyInfo->m_varCC))
{
hr = pMsg->put_CC(V_BSTR(&pNotifyInfo->m_varCC));
_JumpIfError(hr, error, "put_CC");
}
///////////////////////////////////////////////////////////////////////////
// Set message body
hr = pNotifyInfo->BuildMessageBody(pServer, bstrBody);
_JumpIfError(hr, error, "CNotifyInfo::BuildMessageBody");
hr = pMsg->put_TextBody(bstrBody);
_JumpIfError(hr, error, "put_Body");
///////////////////////////////////////////////////////////////////////
// Set body part to UTF-8 charset
hr = pMsg->get_TextBodyPart(&pBp);
_JumpIfError(hr, error, "get_BodyPart");
bstrCharSet = SysAllocString(L"utf-8");
if (NULL == bstrCharSet)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "Exit:SysAllocString");
}
hr = pBp->put_Charset(bstrCharSet);
_JumpIfError(hr, error, "put_Charset");
///////////////////////////////////////////////////////////////////////////
// Set message title
hr = pNotifyInfo->BuildMessageTitle(pServer, bstrTitle);
_JumpIfError(hr, error, "CNotifyInfo::BuildMessageTitle");
hr = pMsg->put_Subject(bstrTitle);
_JumpIfError(hr, error, "put_Subject");
///////////////////////////////////////////////////////////////////////////
// Send SMTP message
DBGPRINT((DBG_SS_CERTEXIT,
"---MAIL NOTIFICATION---\nTo: %ws\nFrom: %ws\nCC: %ws\n%ws\n%ws\n---END MAIL NOTIFICATION---\n",
bstrSetTo,
bstrSetFrom,
VT_BSTR==V_VT(&pNotifyInfo->m_varCC)?V_BSTR(&pNotifyInfo->m_varCC):L"",
bstrTitle,
bstrBody));
fRetryReloadCDOConfig = false;
while(true)
{
if(m_fReloadCDOConfig || fRetryReloadCDOConfig)
{
hr = _InitCDO();
_JumpIfError(hr, error, "CEmailNotify::_InitCDO");
}
// protect m_pICDOCOnfig from being updated
m_rwlockCDOConfig.GetShared();
fRWLockAcquired = true;
hr = pMsg->putref_Configuration(m_pICDOConfig);
_JumpIfError(hr, error, "putref_Configuration");
hr = pMsg->Send();
fRWLockAcquired = false;
m_rwlockCDOConfig.Release();
if(CDO_E_SMTP_SEND_FAILED == hr ||
CDO_E_CONNECTION_DROPPED == hr ||
CDO_E_FAILED_TO_CONNECT == hr)
{
// if this is the first time and it failed due to server connection
// problems, try again
if(!fRetryReloadCDOConfig)
{
_PrintError(hr,
"Failed to send mail, reconnecting to server");
fRetryReloadCDOConfig = true;
continue;
}
m_fReloadCDOConfig = true;
}
_JumpIfError(hr, error, "Send");
break;
}
}
hr = S_OK;
error:
if (S_OK != hr)
{
WCHAR awc[cwcDWORDSPRINTF];
WCHAR *apwsz[2];
switch (lExitEvent)
{
case EXITEVENT_INVALID:
apwsz[0] = L"EXITEVENT_INVALID";
break;
case EXITEVENT_CERTISSUED:
apwsz[0] = L"EXITEVENT_CERTISSUED";
break;
case EXITEVENT_CERTPENDING:
apwsz[0] = L"EXITEVENT_CERTPENDING";
break;
case EXITEVENT_CERTDENIED:
apwsz[0] = L"EXITEVENT_CERTDENIED";
break;
case EXITEVENT_CERTREVOKED:
apwsz[0] = L"EXITEVENT_CERTREVOKED";
break;
case EXITEVENT_CERTRETRIEVEPENDING:
apwsz[0] = L"EXITEVENT_CERTRETRIEVEPENDING";
break;
case EXITEVENT_CRLISSUED:
apwsz[0] = L"EXITEVENT_CRLISSUED";
break;
case EXITEVENT_SHUTDOWN:
apwsz[0] = L"EXITEVENT_SHUTDOWN";
break;
case EXITEVENT_STARTUP:
apwsz[0] = L"EXITEVENT_STARTUP";
break;
default:
wsprintf(awc, L"0x%x", lExitEvent);
apwsz[0] = awc;
break;
}
apwsz[1] = bstrSetTo;
if (NULL == bstrSetTo)
{
apwsz[1] = wszREGEXITPROPNOTFOUND;
}
::LogModuleStatus(
g_hInstance,
S_OK,
MSG_UNABLE_TO_MAIL_NOTIFICATION,
FALSE, // fPolicy
pwszDescription,
apwsz,
NULL);
}
if(fRWLockAcquired)
{
m_rwlockCDOConfig.Release();
}
if(pServer)
{
pServer->Release();
}
if(pMsg)
{
pMsg->Release();
}
if (NULL != pBp)
{
pBp->Release();
}
if (NULL != bstrCharSet)
{
SysFreeString(bstrCharSet);
}
if (bstrTo)
{
SysFreeString(bstrTo);
}
if (bstrBody)
{
SysFreeString(bstrBody);
}
if (bstrTitle)
{
SysFreeString(bstrTitle);
}
if (bstrTemplate)
{
SysFreeString(bstrTemplate);
}
VariantClear(&varCert);
return hr;
}
LONG CNotifyInfo::FormattedMessageInfo::m_gPropTypes[] =
{
PROPTYPE_LONG,
PROPTYPE_DATE,
PROPTYPE_BINARY,
PROPTYPE_STRING,
};
LPCWSTR CNotifyInfo::FormattedMessageInfo::m_gwszArchivedKeyPresent = L"1";
/////////////////////////////////////////////////////////////////////////////
CNotifyInfo::CNotifyInfo()
{
VariantInit(&m_varFrom);
VariantInit(&m_varTo);
VariantInit(&m_varCC);
}
/////////////////////////////////////////////////////////////////////////////
CNotifyInfo::~CNotifyInfo()
{
VariantClear(&m_varFrom);
VariantClear(&m_varTo);
VariantClear(&m_varCC);
}
/////////////////////////////////////////////////////////////////////////////
HRESULT CNotifyInfo::LoadInfoFromRegistry(
HKEY hkeySMTP,
LPCWSTR pcwszSubkey)
{
HRESULT hr;
HKEY hkeyEventInfo = NULL;
VARIANT varBodyFormatTmp;
VariantInit(&varBodyFormatTmp);
hr = RegOpenKeyEx(
hkeySMTP,
pcwszSubkey,
0, // dwReserved
KEY_READ | KEY_QUERY_VALUE,
&hkeyEventInfo);
_JumpIfErrorStr(hr, error, "RegOpenKey", pcwszSubkey);
hr = RegGetValue(
hkeyEventInfo,
wszREGEXITSMTPFROM,
&m_varFrom);
if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr)
{
hr = S_OK;
}
_JumpIfErrorStr(hr, error, "RegGetValue", wszREGEXITSMTPFROM);
hr = RegGetValue(
hkeyEventInfo,
wszREGEXITSMTPTO,
&m_varTo);
if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr)
{
hr =S_OK;
}
_JumpIfErrorStr(hr, error, "RegGetValue", wszREGEXITSMTPTO);
hr = RegGetValue(
hkeyEventInfo,
wszREGEXITSMTPCC,
&m_varCC);
if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr)
{
hr =S_OK;
}
_JumpIfErrorStr(hr, error, "RegGetValue", wszREGEXITSMTPCC);
hr = RegGetValue(
hkeyEventInfo,
wszREGEXITTITLEFORMAT,
&m_TitleFormat.m_varFormat);
if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr ||
VT_BSTR != V_VT(&m_TitleFormat.m_varFormat))
{
hr =S_OK;
VariantClear(&m_TitleFormat.m_varFormat);
V_VT(&m_TitleFormat.m_varFormat) = VT_BSTR;
V_BSTR(&m_TitleFormat.m_varFormat) = SysAllocString(L"");
_JumpIfAllocFailed(V_BSTR(&m_TitleFormat.m_varFormat), error);
}
_JumpIfErrorStr(hr, error, "RegGetValue", wszREGEXITTITLEFORMAT);
hr = RegGetValue(
hkeyEventInfo,
wszREGEXITTITLEARG,
&m_TitleFormat.m_varArgs);
if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr ||
(VT_ARRAY|VT_BSTR) != V_VT(&m_TitleFormat.m_varArgs))
{
VariantClear(&m_TitleFormat.m_varArgs);
hr =S_OK;
}
_JumpIfErrorStr(hr, error, "RegGetValue", wszREGEXITTITLEARG);
hr = RegGetValue(
hkeyEventInfo,
wszREGEXITBODYFORMAT,
&varBodyFormatTmp);
if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr ||
((VT_ARRAY|VT_BSTR) != V_VT(&varBodyFormatTmp)&&
(VT_BSTR) != V_VT(&varBodyFormatTmp)))
{
hr = S_OK;
V_VT(&m_BodyFormat.m_varFormat) = VT_BSTR;
V_BSTR(&m_BodyFormat.m_varFormat) = SysAllocString(L"");
_JumpIfAllocFailed(V_BSTR(&m_BodyFormat.m_varFormat), error);
}
else if(S_OK == hr)
{
if((VT_ARRAY|VT_BSTR) == V_VT(&varBodyFormatTmp))
{
// code down the road expects this to be a BSTR
// so we concatenate the strings, separated by new lines (\n)
hr = _ConvertBSTRArrayToBSTR(
varBodyFormatTmp,
m_BodyFormat.m_varFormat);
}
else // VT_BSTR
{
hr = VariantCopy(
&m_BodyFormat.m_varFormat,
&varBodyFormatTmp);
}
}
_JumpIfErrorStr(hr, error, "RegGetValue", wszREGEXITBODYFORMAT);
hr = RegGetValue(
hkeyEventInfo,
wszREGEXITBODYARG,
&m_BodyFormat.m_varArgs);
if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr)
{
hr =S_OK;
}
_JumpIfErrorStr(hr, error, "RegGetValue", wszREGEXITBODYARG);
hr = S_OK;
error:
if (hkeyEventInfo)
{
RegCloseKey(hkeyEventInfo);
}
VariantClear(&varBodyFormatTmp);
return hr;
}
/////////////////////////////////////////////////////////////////////////////
HRESULT CNotifyInfo::_ConvertBSTRArrayToBSTR(VARIANT& varIn, VARIANT& varOut)
{
HRESULT hr;
SafeArrayEnum<BSTR>
saenumArgs(V_ARRAY(&varIn));
BSTR bstrArg; //no free
DWORD cchBufSize = 1;
LPWSTR pwszOut = NULL;
WCHAR *pchCrt;
for(hr = saenumArgs.Next(bstrArg);
S_OK==hr;
hr = saenumArgs.Next(bstrArg))
{
cchBufSize += SysStringLen(bstrArg)+wcslen(L"\n");
}
pwszOut = (LPWSTR) LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT,
cchBufSize*sizeof(WCHAR));
_JumpIfAllocFailed(pwszOut, error);
saenumArgs.Reset();
pchCrt = pwszOut;
for(hr = saenumArgs.Next(bstrArg);
S_OK==hr;
hr = saenumArgs.Next(bstrArg))
{
wcscat(pchCrt, bstrArg);
wcscat(pchCrt, L"\n");
}
V_VT(&varOut) = VT_BSTR;
V_BSTR(&varOut) = SysAllocString(pwszOut);
_JumpIfAllocFailed(V_BSTR(&varOut), error);
hr = S_OK;
error:
LOCAL_FREE(pwszOut);
return hr;
}
/////////////////////////////////////////////////////////////////////////////
HRESULT CNotifyInfo::BuildMessageTitle(ICertServerExit* pServer, BSTR& rbstrOut)
{
return m_TitleFormat.BuildFormattedString(
pServer,
rbstrOut);
}
/////////////////////////////////////////////////////////////////////////////
HRESULT CNotifyInfo::BuildMessageBody (ICertServerExit* pServer, BSTR& rbstrOut)
{
return m_BodyFormat.BuildFormattedString(
pServer,
rbstrOut);
}
/////////////////////////////////////////////////////////////////////////////
HRESULT
CNotifyInfo::FormattedMessageInfo::_FormatStringFromArgs(
IN LPWSTR *ppwszArgs,
OPTIONAL OUT WCHAR *pwszOut,
IN OUT DWORD *pcwcOut)
{
HRESULT hr;
WCHAR const *pwszFmt;
DWORD cwcOut;
DWORD cwcBuf;
WCHAR const *pwszOutOrg = pwszOut;
cwcBuf = 0;
if (NULL != pwszOut)
{
cwcBuf = *pcwcOut;
}
cwcOut = 0;
pwszFmt = V_BSTR(&m_varFormat);
if (NULL != pwszFmt)
{
while (L'\0' != *pwszFmt)
{
DWORD cwcCopy;
DWORD cwcSkip;
WCHAR const *pwszT;
WCHAR const *pwszArg;
cwcSkip = 0;
pwszArg = NULL;
pwszT = wcschr(pwszFmt, L'%');
if (NULL != pwszT)
{
LONG iArg;
cwcCopy = SAFE_SUBTRACT_POINTERS(pwszT, pwszFmt);
pwszT++;
iArg = _wtoi(pwszT);
if (0 < iArg && m_nArgs >= iArg)
{
pwszArg = ppwszArgs[iArg - 1];
cwcSkip++;
while (iswdigit(*pwszT))
{
pwszT++;
cwcSkip++;
}
}
else
{
cwcCopy++;
if (L'%' == *pwszT)
{
cwcSkip++;
}
else
{
_PrintErrorStr(
E_INVALIDARG,
"Exit:Bad Arg specifier",
&pwszT[-1]);
}
}
}
else
{
cwcCopy = wcslen(pwszFmt);
}
if (NULL != pwszOut)
{
if (cwcCopy >= cwcBuf)
{
_PrintErrorStr(
HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER),
"Exit:overflow",
pwszFmt);
pwszOut = NULL;
}
else
{
CopyMemory(pwszOut, pwszFmt, cwcCopy * sizeof(WCHAR));
pwszOut += cwcCopy;
cwcBuf -= cwcCopy;
}
}
pwszFmt += cwcCopy + cwcSkip;
cwcOut += cwcCopy;
if (NULL != pwszArg)
{
cwcCopy = wcslen(pwszArg);
if (NULL != pwszOut)
{
if (cwcCopy >= cwcBuf)
{
pwszOut = NULL;
_PrintErrorStr(
HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER),
"Exit:overflow",
pwszFmt);
}
else
{
CopyMemory(pwszOut, pwszArg, cwcCopy * sizeof(WCHAR));
pwszOut += cwcCopy;
cwcBuf -= cwcCopy;
}
}
cwcOut += cwcCopy;
}
}
}
if (NULL != pwszOut)
{
if (1 > cwcBuf)
{
pwszOut = NULL;
_PrintError(
HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER),
"Exit:overflow end");
}
else
{
*pwszOut = L'\0';
}
}
*pcwcOut = cwcOut;
if (NULL == pwszOut)
{
(*pcwcOut)++;
if (NULL != pwszOutOrg)
{
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
_JumpError(hr, error, "Exit:pwszOut");
}
}
hr = S_OK;
error:
return(hr);
}
/////////////////////////////////////////////////////////////////////////////
HRESULT
CNotifyInfo::FormattedMessageInfo::BuildFormattedString(
ICertServerExit* pServer,
BSTR& rbstrOut)
{
HRESULT hr;
LPWSTR *ppwszArgs = NULL;
DWORD cwcOut;
hr = BuildArgList(pServer, ppwszArgs);
_JumpIfError(hr, error, "BuildArgList");
hr = _FormatStringFromArgs(ppwszArgs, NULL, &cwcOut);
_JumpIfError(hr, error, "_FormatStringFromArgs");
if (0 < cwcOut) // count included L'\0' terminator
{
cwcOut--;
}
rbstrOut = SysAllocStringLen(NULL, cwcOut);
if (NULL == rbstrOut)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "Exit:LocalAlloc");
}
cwcOut++; // buffer must include L'\0' terminator
hr = _FormatStringFromArgs(ppwszArgs, rbstrOut, &cwcOut);
_JumpIfError(hr, error, "_FormatStringFromArgs");
// count no longer includes L'\0' terminator
CSASSERT(SysStringLen(rbstrOut) == wcslen(rbstrOut));
CSASSERT(SysStringLen(rbstrOut) == cwcOut);
error:
FreeArgList(ppwszArgs);
if (S_OK != hr)
{
if (NULL != rbstrOut)
{
SysFreeString(rbstrOut);
rbstrOut = NULL;
}
}
return(hr);
}
/////////////////////////////////////////////////////////////////////////////
void
CNotifyInfo::FormattedMessageInfo::FreeArgList(
LPWSTR*& rppwszArgs)
{
if(rppwszArgs)
{
for(LONG cArgs = 0;cArgs<m_nArgs;cArgs++)
{
if(rppwszArgs[cArgs])
{
LocalFree(rppwszArgs[cArgs]);
rppwszArgs[cArgs] = NULL;
}
}
LocalFree(rppwszArgs);
rppwszArgs = NULL;
}
}
HRESULT CNotifyInfo::FormattedMessageInfo::InitializeArgInfo(
ICertServerExit* pServer)
{
HRESULT hr;
SafeArrayEnum<BSTR>
saenumArgs(V_ARRAY(&m_varArgs));
BSTR bstrArg; //no free
LONG cArgs;
VARIANT varValue;
GetCertOrRequestProp pGetPropertyFunc;
EnterCriticalSection(&m_critsectObjInit);
if(!m_fInitialized)
{
if(VT_EMPTY == V_VT(&m_varArgs))
{
m_nArgs = 0;
}
else
{
m_nArgs = saenumArgs.GetCount();
CSASSERT(!m_pfArgFromRequestTable);
m_pfArgFromRequestTable = (bool*)
LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT,
sizeof(bool)*m_nArgs);
_JumpIfAllocFailed(m_pfArgFromRequestTable, error);
CSASSERT(!m_pArgType);
m_pArgType = (LONG*)
LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT,
sizeof(LONG)*m_nArgs);
_JumpIfAllocFailed(m_pArgType, error);
VariantInit(&varValue);
for(cArgs=0, hr = saenumArgs.Next(bstrArg);
S_OK==hr;
cArgs++, hr = saenumArgs.Next(bstrArg))
{
BSTR bstrPropertyName = bstrArg;
VariantInit(&varValue);
m_pfArgFromRequestTable[cArgs] =
(0 == _wcsnicmp(bstrArg,
wszPROPREQUESTDOT,
wcslen(wszPROPREQUESTDOT)));
if(m_pfArgFromRequestTable[cArgs])
{
// properties from request table start with request.
bstrPropertyName += wcslen(wszPROPREQUESTDOT);
}
pGetPropertyFunc =
m_pfArgFromRequestTable[cArgs]?
(&(ICertServerExit::GetRequestProperty)):
(&(ICertServerExit::GetCertificateProperty));
// we don't know the type of property yet, figure it out
// by trying each type
for(LONG cType = 0; cType<ARRAYSIZE(m_gPropTypes); cType++)
{
hr = (pServer->*pGetPropertyFunc)(
bstrPropertyName,
m_gPropTypes[cType],
&varValue);
if(S_OK == hr ||
CERTSRV_E_PROPERTY_EMPTY == hr) // found the type
{
m_pArgType[cArgs] = m_gPropTypes[cType];
DBGPRINT((DBG_SS_CERTEXIT, "Property %s has type %d\n",
bstrPropertyName, m_gPropTypes[cType]));
break;
}
}
// if not found, default will be 0 (invalid type)
VariantClear(&varValue);
}
}
m_fInitialized = true;
}
hr = S_OK;
error:
if(S_OK != hr)
{
LOCAL_FREE(m_pfArgFromRequestTable);
m_pfArgFromRequestTable = NULL;
LOCAL_FREE(m_pArgType);
m_pArgType = NULL;
}
LeaveCriticalSection(&m_critsectObjInit);
return hr;
}
/////////////////////////////////////////////////////////////////////////////
HRESULT
CNotifyInfo::FormattedMessageInfo::BuildArgList(
ICertServerExit* pServer,
LPWSTR*& rppwszArgs)
{
HRESULT hr;
SafeArrayEnum<BSTR>
saenumArgs(V_ARRAY(&m_varArgs));
BSTR bstrArg; //no free
LONG cArgs;
VARIANT varValue;
GetCertOrRequestProp pGetPropertyFunc;
rppwszArgs = NULL;
// REG_SZ, ie VT_BSTR
if(VT_BSTR != V_VT(&m_varFormat))
{
hr = HRESULT_FROM_WIN32(ERROR_BADKEY);
_JumpError(hr, error, "invalid message format");
}
// REG_MULTISZ, ie VT_ARRAY|VT_BSTR or VT_EMPTY if not found
if((VT_ARRAY|VT_BSTR) != V_VT(&m_varArgs) &&
VT_EMPTY != V_VT(&m_varArgs))
{
hr = HRESULT_FROM_WIN32(ERROR_BADKEY);
_JumpError(hr, error, "invalid message arg");
}
if(!m_fInitialized)
{
hr = InitializeArgInfo(pServer);
_JumpIfError(hr, error, "FormattedMessageInfo::InitializeArgInfo");
}
saenumArgs.Reset();
if(m_nArgs>0)
{
rppwszArgs = (LPWSTR*) LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT,
m_nArgs*sizeof(LPWSTR));
_JumpIfAllocFailed(rppwszArgs, error);
for(cArgs=0, hr = saenumArgs.Next(bstrArg);
S_OK==hr;
cArgs++, hr = saenumArgs.Next(bstrArg))
{
BSTR bstrPropertyName = bstrArg;
LONG lType = m_pArgType[cArgs];
VariantInit(&varValue);
if(m_pfArgFromRequestTable[cArgs])
{
// properties from request table start with request.
bstrPropertyName += wcslen(wszPROPREQUESTDOT);
}
pGetPropertyFunc =
m_pfArgFromRequestTable[cArgs]?
(&(ICertServerExit::GetRequestProperty)):
(&(ICertServerExit::GetCertificateProperty));
hr = (pServer->*pGetPropertyFunc)(
bstrPropertyName,
m_pArgType[cArgs],
&varValue);
if(S_OK != hr)
{
lType = PROPTYPE_STRING;
V_VT(&varValue) = VT_BSTR;
V_BSTR(&varValue) = SysAllocString(wszREGEXITPROPNOTFOUND);
_JumpIfAllocFailed(V_BSTR(&varValue), error);
hr = S_OK;
}
hr = ConvertToString(
&varValue,
lType,
bstrPropertyName,
&rppwszArgs[cArgs]);
if(S_OK != hr)
{
rppwszArgs[cArgs] = (LPWSTR) LocalAlloc(LMEM_FIXED,
sizeof(WCHAR)*(wcslen(wszREGEXITPROPNOTFOUND)+1));
_JumpIfAllocFailed(rppwszArgs[cArgs], error);
wcscpy(rppwszArgs[cArgs], wszREGEXITPROPNOTFOUND);
}
VariantClear(&varValue);
}
}
hr = S_OK;
error:
if(S_OK != hr)
{
FreeArgList(rppwszArgs);
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
HRESULT CNotifyInfo::FormattedMessageInfo::ConvertToString(
VARIANT* pvarValue,
LONG lType,
LPCWSTR pcwszPropertyName,
LPWSTR* ppwszValue)
{
HRESULT hr = E_FAIL;
switch(lType)
{
case PROPTYPE_LONG:
if(0 == _wcsicmp(pcwszPropertyName, wszPROPREQUESTSTATUSCODE))
{
*ppwszValue = const_cast<WCHAR*>(myGetErrorMessageText(
V_I4(pvarValue),
TRUE));
if(!*ppwszValue)
{
hr = E_OUTOFMEMORY;
}
else
{
hr = S_OK;
}
}
else
{
hr = ConvertToStringI2I4(
V_I4(pvarValue),
ppwszValue);
}
break;
case PROPTYPE_DATE:
hr = ConvertToStringDATE(&V_DATE(pvarValue), TRUE, ppwszValue);
break;
case PROPTYPE_BINARY:
if(0 == _wcsicmp(pcwszPropertyName, wszPROPREQUESTRAWARCHIVEDKEY))
{
hr = myDupString(m_gwszArchivedKeyPresent, ppwszValue);
}
else
{
hr = myCryptBinaryToString(
(const BYTE*) V_BSTR(pvarValue),
SysStringByteLen(V_BSTR(pvarValue)),
CRYPT_STRING_BASE64,
ppwszValue);
}
break;
case PROPTYPE_STRING:
if(0 == _wcsicmp(pcwszPropertyName, wszPROPCERTTEMPLATE))
{
hr = GetCertTypeFriendlyName(
V_BSTR(pvarValue),
ppwszValue);
} // fall through
if(S_OK != hr)
{
hr = ConvertToStringWSZ(
V_BSTR(pvarValue),
ppwszValue);
}
break;
}
return hr;
}