2025-04-27 07:49:33 -04:00

1579 lines
38 KiB
C++

//+--------------------------------------------------------------------------
//
// 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 <atlbase.h>
#include <atlimpl.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
#define CERTTYPE_ATTR_NAME TEXT("CertificateTemplate")
#define MAX_CRL_PROP (32 + 10)
#define cbVALUEZEROPAD (3 * sizeof(WCHAR))
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);
}
VariantClear(&m_varFrom);
VariantClear(&m_varCC);
VariantClear(&m_varSubject);
if(m_pICDOConfig)
{
m_pICDOConfig->Release();
}
}
//+--------------------------------------------------------------------------
// 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;
HKEY hkey = NULL;
DWORD cbbuf;
DWORD dwType;
ENUM_CATYPES CAType;
LPWSTR pwszCLSIDCertExit = NULL;
ICertServerExit* pServer = NULL;
VARIANT varValue;
WCHAR sz[MAX_PATH];
VariantInit(&varValue);
#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
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 = 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 ((HRESULT) 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 = _EMailInit(); // no_sdksample
_JumpIfError(hr, error, "Exit:_EMailInit");
error:
if (NULL != hkey)
{
RegCloseKey(hkey);
}
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);
}
//+--------------------------------------------------------------------------
// 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 wszPath[MAX_PATH];
WCHAR const *pwszFile;
WCHAR const *pwsz;
hr = pServer->GetRequestAttribute(wszPROPEXITCERTFILE, &strCertFile);
if (S_OK != hr)
{
DBGPRINT((
DBG_SS_CERTEXIT,
"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 = myHLastError();
_JumpErrorStr(hr, error, "Exit:CreateFile", wszPath);
}
if (!WriteFile(hFile, pbCert, cbCert, &cbWritten, NULL))
{
hr = myHLastError();
_JumpErrorStr(hr, error, "Exit:WriteFile", wszPath);
}
if (cbWritten != cbCert)
{
hr = STG_E_WRITEFAULT;
DBGPRINT((
DBG_SS_CERTEXIT,
"Exit:WriteFile(%ws): attempted %x, actual %x bytes: %x\n",
wszPath,
cbCert,
cbWritten,
hr));
goto error;
}
error:
// end_sdksample
if (hr != S_OK)
{
LPCWSTR wszStrings[1];
wszStrings[0] = wszPath;
::LogModuleStatus(
g_hInstance,
MSG_UNABLE_TO_WRITEFILE,
FALSE,
m_strDescription,
wszStrings);
}
// begin_sdksample
if (INVALID_HANDLE_VALUE != hFile)
{
CloseHandle(hFile);
}
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;
HRESULT hr2;
VARIANT varCert;
VARIANT varCertType;
ICertServerExit *pServer = NULL;
BSTR strCertType;
VariantInit(&varCert);
VariantInit(&varCertType);
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((DBG_SS_CERTEXIT, "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;
}
// end_sdksample
if (m_fEMailNotify)
{
hr2 = _EMailNotify(pServer, strCertType);
_PrintIfError(hr2, "_EMailNotify");
if (S_OK == hr)
{
hr = hr2;
}
}
// begin_sdksample
error:
VariantClear(&varCert);
VariantClear(&varCertType);
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;
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);
}
//+--------------------------------------------------------------------------
// 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:
hr = _NotifyCRLIssued(Context);
psz = "crlissued";
break;
case EXITEVENT_SHUTDOWN:
psz = "shutdown";
break;
}
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);
}
// end_sdksample
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 CCertExit::_RegGetValue(
HKEY hkey,
LPCWSTR pcwszValName,
VARIANT* pvarValue)
{
HRESULT hr;
DWORD dwType;
DWORD cbVal;
BYTE* pbVal = NULL;
hr = RegQueryValueEx(hkey, pcwszValName, NULL, &dwType, NULL, &cbVal);
if (S_OK != hr)
{
hr = myHError(hr);
_JumpErrorStr(hr, error, "RegQueryValueEx", pcwszValName);
}
pbVal = (BYTE*) LocalAlloc(LMEM_FIXED, cbVal);
_JumpIfAllocFailed(pbVal, error);
hr = RegQueryValueEx(hkey, pcwszValName, NULL, &dwType, pbVal, &cbVal);
if (S_OK != hr)
{
hr = myHError(hr);
_JumpErrorStr(hr, error, "RegQueryValueEx", pcwszValName);
}
hr = myRegValueToVariant(
dwType,
cbVal,
pbVal,
pvarValue);
_JumpIfError(hr, error, "myRegValueToVariant");
error:
if(pbVal)
{
LocalFree(pbVal);
}
return hr;
}
/*HRESULT CCertExit::_LoadBodyFieldsFromRegistry(HKEY hkeySMTP)
{
HRESULT hr;
HKEY hkeyBodyFields = NULL;
DWORD cValues;
DWORD cbValue;
DWORD dwValCount;
DWORD dwType;
// Enumerate all Body fields under SMTP\BodyFields key. Value names
// are ordered and named 1, 2, 3 and so on. All values should be strings only
hr = RegOpenKeyEx(
hkeySMTP,
wszREGEXITSMTPBODYFIELDSKEY,
0,
KEY_QUERY_VALUE|KEY_READ,
&hkeyBodyFields);
if(S_OK != hr)
{
hr = myHError(hr);
_JumpErrorStr(hr, error, "RegOpenKeyEx", wszREGEXITSMTPKEY);
}
hr = RegQueryInfoKey(
hkeyBodyFields,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&cValues,
NULL,
&cbValue,
NULL,
NULL);
_JumpIfErrorStr(hr, error, "RegQueryInfoKey", wszREGEXITSMTPKEY);
m_pawszBodyFields = (LPWSTR*)LocalAlloc(LMEM_FIXED, sizeof(LPWSTR)*cValues);
_JumpIfAllocFailed(m_pawszBodyFields, error);
ZeroMemory(m_pawszBodyFields, sizeof(LPWSTR)*cValues);
for(dwValCount; dwValCount<cValues; dwValCount++)
{
DWORD cbValueTemp = cbValue;
WCHAR wszValName[20];
wsprintf(wszVal, L"%d", dwValCount);
m_pawszBodyFields[dwValCount] = (LPWSTR)LocalAlloc(LMEM_FIXED, cbValueTemp);
_JumpIfAllocFailed(m_pawszBodyFields[dwValCount] , error);
hr = RegQueryValueEx(
hkeyBodyFields,
wszValName,
NULL,
&dwType,
m_pawszBodyFields[dwValCount],
&cbValueTemp);
if (hr != S_OK)
{
hr = myHError(hr);
_JumpError(hr, error, "RegEnumValue");
}
if(dwType != REG_SZ)
{
hr = HRESULT_FROM_WIN32(ERROR_BADKEY);
_JumpError(hr, error, "nonstring value type");
}
}
error:
if(hkeyBodyFields)
{
RegCloseKey(hkeyBodyFields);
}
return hr;
}*/
HRESULT CCertExit::_LoadFieldsFromRegistry(Fields* pFields)
{
HRESULT hr;
DWORD dwType;
BYTE* pbValue = NULL;
DWORD cbValue;
VARIANT varValue;
DWORD dwIndex;
HKEY hkeySMTP = NULL;
LPWSTR pwszValName = NULL;
DWORD cbValName;
DWORD cValues;
static LPCWSTR pcwszHTTP = L"http://";
static size_t cHTTP = wcslen(pcwszHTTP);
VariantInit(&varValue);
// enumerate all CDO fields under SMTP key (value name is full HTTP URL)
hr = RegOpenKeyEx(
m_hExitKey,
wszREGEXITSMTPKEY,
0,
KEY_QUERY_VALUE|KEY_READ,
&hkeySMTP);
if(S_OK != hr)
{
hr = myHError(hr);
_JumpErrorStr(hr, error, "RegOpenKeyEx", wszREGEXITSMTPKEY);
}
hr = RegQueryInfoKey(
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(
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 = _LoadBodyFieldsFromRegistry(hkeySMTP)
_JumpIfError(hr, error, "_LoadBodyFieldsFromRegistry");
// retrieve Body field - mandatory
hr = _RegGetValue(
hkeySMTP,
wszREGEXITSMTPBODY,
&m_varBody);
_JumpIfErrorStr(hr, error, "_RegGetValue", wszREGEXITSMTPBODY);
if(V_VT(&m_varBody) != VT_BSTR)
{
VariantClear(&m_varBody);
hr = HRESULT_FROM_WIN32(ERROR_BADKEY);
_JumpErrorStr(hr, error, "invalid From field", wszREGEXITSMTPBODY);
}*/
// retrieve From field - optional; if not present, From field will
// be built: ca_name@machine_dns_name
hr = _RegGetValue(
hkeySMTP,
wszREGEXITSMTPFROM,
&m_varFrom);
if(S_OK==hr &&
V_VT(&m_varCC) != VT_BSTR)
{
VariantClear(&m_varCC);
hr = HRESULT_FROM_WIN32(ERROR_BADKEY);
_JumpErrorStr(hr, error, "invalid CC field", wszREGEXITSMTPCC);
}
// retrieve CC field - optional
hr = _RegGetValue(
hkeySMTP,
wszREGEXITSMTPCC,
&m_varCC);
if(S_OK==hr &&
V_VT(&m_varCC) != VT_BSTR)
{
VariantClear(&m_varCC);
hr = HRESULT_FROM_WIN32(ERROR_BADKEY);
_JumpErrorStr(hr, error, "invalid CC field", wszREGEXITSMTPCC);
}
// retrieve Subject field - optional
hr = _RegGetValue(
hkeySMTP,
wszREGEXITSMTPSUBJECT,
&m_varSubject);
if(S_OK==hr &&
V_VT(&m_varCC) != VT_BSTR)
{
VariantClear(&m_varCC);
hr = HRESULT_FROM_WIN32(ERROR_BADKEY);
_JumpErrorStr(hr, error, "invalid CC field", wszREGEXITSMTPCC);
}
hr = S_OK;
error:
if(hkeySMTP)
{
RegCloseKey(hkeySMTP);
}
VariantClear(&varValue);
if(pwszValName)
{
LocalFree(pwszValName);
}
if(pbValue)
{
LocalFree(pbValue);
}
return hr;
}
HRESULT CCertExit::_SetField(
Fields* pFields,
LPCWSTR pcwszFieldSchemaName,
VARIANT *pvarFieldValue)
{
HRESULT hr;
Field* pfld = NULL;
hr = pFields->get_Item(CComVariant(pcwszFieldSchemaName),&pfld);
_JumpIfErrorStr(hr, error, "CDO::Field::get_Item", pcwszFieldSchemaName);
hr = pfld->put_Value(*pvarFieldValue);
_JumpIfErrorStr(hr, error, "CDO::Field::put_Value", pcwszFieldSchemaName);
error:
if(pfld)
{
pfld->Release();
}
return hr;
}
HRESULT CCertExit::_LoadFieldsFromLSASecret(
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");
}
error:
if (NULL != pwszProfileName)
{
LocalFree(pwszProfileName);
}
if (NULL != pwszLogonName)
{
LocalFree(pwszLogonName);
}
if (NULL != pwszPassword)
{
DWORD cwc = wcslen(pwszPassword);
ZeroMemory(pwszPassword, cwc * sizeof(WCHAR));
LocalFree(pwszPassword);
}
if(NULL != bstrLogonName)
{
SysFreeString(bstrLogonName);
}
if(NULL != bstrPassword)
{
DWORD cwc = wcslen(bstrPassword);
ZeroMemory(bstrPassword, cwc * sizeof(WCHAR));
SysFreeString(bstrPassword);
}
return hr;
}
HRESULT CCertExit::_BuildCAMailAddressAndSubject()
{
HRESULT hr;
LPWSTR pwszMachineDNSName = NULL;
LPWSTR pwszMailAddr = NULL;
// If not specified in the registry, build SMTP "From" field:
//
// CA_name@machine_dns_name
//
if(V_VT(&m_varFrom)==VT_EMPTY ||
V_VT(&m_varSubject)==VT_EMPTY)
{
hr = myGetMachineDnsName(&pwszMachineDNSName);
_JumpIfError(hr, error, "myGetMachineDnsName");
pwszMailAddr = (LPWSTR)LocalAlloc(
LMEM_FIXED,
sizeof(WCHAR)*(wcslen(m_strCAName)+wcslen(pwszMachineDNSName)+4));
_JumpIfAllocFailed(pwszMailAddr, error);
wcscpy(pwszMailAddr, m_strCAName);
wcscat(pwszMailAddr, L"@");
wcscat(pwszMailAddr, pwszMachineDNSName);
}
if(V_VT(&m_varFrom)==VT_EMPTY)
{
V_BSTR(&m_varFrom) = SysAllocString(pwszMailAddr);
_JumpIfAllocFailed(V_BSTR(&m_varFrom), error);
V_VT(&m_varFrom) = VT_BSTR;
}
if(V_VT(&m_varSubject)==VT_EMPTY)
{
V_BSTR(&m_varSubject) = SysAllocString(pwszMailAddr);
_JumpIfAllocFailed(V_BSTR(&m_varSubject), error);
V_VT(&m_varSubject) = VT_BSTR;
}
error:
if(pwszMachineDNSName)
{
LocalFree(pwszMachineDNSName);
}
if(pwszMailAddr)
{
LocalFree(pwszMailAddr);
}
return hr;
}
HRESULT
CCertExit::_EMailInit()
{
HRESULT hr = S_OK;
Fields* pFields = NULL;
if ((EXITPUB_EMAILNOTIFYSMARTCARD |
EXITPUB_EMAILNOTIFYALL) &
m_dwExitPublishFlags)
{
m_fEMailNotify = TRUE;
}
if (m_fEMailNotify)
{
hr = CoCreateInstance(CDO::CLSID_Configuration,
NULL,
CLSCTX_INPROC_SERVER,
CDO::IID_IConfiguration,
reinterpret_cast<void**>(&m_pICDOConfig));
_JumpIfError(hr, error, "CoCreateInstance CDO_IConfiguration");
hr = m_pICDOConfig->get_Fields(&pFields);
_JumpIfError(hr, error, "CDO::IConfig::get_Fields");
hr = _LoadFieldsFromRegistry(pFields);
_JumpIfError(hr, error, "_LoadFieldsFromRegistry");
hr = _LoadFieldsFromLSASecret(pFields);
_JumpIfError(hr, error, "_LoadFieldsFromLSASecret");
hr = _BuildCAMailAddressAndSubject();
_JumpIfError(hr, error, "_BuildFromField");
hr = pFields->Update();
_JumpIfError(hr, error, "config");
}
error:
if (S_OK != hr)
{
m_fEMailNotify = FALSE;
if(m_pICDOConfig)
{
m_pICDOConfig->Release();
m_pICDOConfig = NULL;
}
}
if(pFields)
{
pFields->Release();
}
return(hr);
}
HRESULT
GetCertTypeFriendlyName(
IN WCHAR const *pwszCertType,
OUT BSTR *pstrFriendlyName)
{
HRESULT hr;
HCERTTYPE hCertType = NULL;
WCHAR **apwszNames = NULL;
CSASSERT(NULL == *pstrFriendlyName);
hr = CAFindCertTypeByName(
pwszCertType,
NULL, // hCAInfo
CT_FIND_LOCAL_SYSTEM |
CT_ENUM_MACHINE_TYPES |
CT_ENUM_USER_TYPES, // dwFlags
&hCertType);
_JumpIfErrorStr(hr, error, "Exit:CAFindCertTypeByName", pwszCertType);
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");
}
*pstrFriendlyName = SysAllocString(apwszNames[0]);
if (NULL == *pstrFriendlyName)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "Exit:SysAllocString");
}
error:
if (NULL != apwszNames)
{
CAFreeCertTypeProperty(hCertType, apwszNames);
}
if (NULL != hCertType)
{
CACloseCertType(hCertType);
}
return(hr);
}
HRESULT
GetStringProperty(
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
{
if (fRequest)
{
hr = pServer->GetRequestProperty(pwszProp, PROPTYPE_STRING, &var);
}
else
{
hr = pServer->GetCertificateProperty(
pwszProp,
PROPTYPE_STRING,
&var);
}
}
if (!fAllowUnknown || CERTSRV_E_PROPERTY_EMPTY != hr)
{
_JumpIfErrorStr(
hr,
error,
fRequest? "Exit:GetRequestProperty" : "Exit:GetCertificateProperty",
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
CCertExit::_EMailNotify(
IN ICertServerExit *pServer,
OPTIONAL IN BSTR strCertType)
{
HRESULT hr;
WCHAR *apwsz[6];
WCHAR *pwszMessage = NULL;
BSTR strEMail = NULL;
BSTR strRequester = NULL;
BSTR strDN = NULL;
BSTR strSerialNumber = NULL;
BSTR strCertTypeFriendlyName = NULL;
IMessage* pMsg = NULL;
hr = GetStringProperty(
pServer,
FALSE,
FALSE,
wszPROPEMAIL,
&strEMail);
_PrintIfErrorStr(hr, "Exit:GetStringProperty", wszPROPEMAIL);
hr = GetStringProperty(
pServer,
TRUE,
TRUE,
wszPROPREQUESTERNAME,
&strRequester);
_JumpIfError(hr, error, "Exit:GetStringProperty");
hr = GetStringProperty(
pServer,
FALSE,
TRUE,
wszPROPDISTINGUISHEDNAME,
&strDN);
_JumpIfError(hr, error, "Exit:GetStringProperty");
hr = GetStringProperty(
pServer,
FALSE,
TRUE,
wszPROPCERTIFICATESERIALNUMBER,
&strSerialNumber);
_JumpIfError(hr, error, "Exit:GetStringProperty");
if (NULL == strCertType)
{
// "Unknown"
hr = GetStringProperty(
pServer,
FALSE,
TRUE,
NULL,
&strCertTypeFriendlyName);
_JumpIfError(hr, error, "Exit:GetStringProperty");
}
else
{
hr = GetCertTypeFriendlyName(strCertType, &strCertTypeFriendlyName);
_PrintIfErrorStr(hr, "Exit:GetCertTypeFriendlyName", strCertType);
}
// %1 is used for a newline!
// A new certificate requested by %2 was issued for\n%3\n
// Certificate Type: %4\n
// Serial Number: %5\n
// Certification Authority: %6 on Server %7\n
apwsz[0] = L"\n";
apwsz[1] = strRequester;
apwsz[2] = strDN;
apwsz[3] = NULL != strCertTypeFriendlyName?
strCertTypeFriendlyName : strCertType;
apwsz[4] = strSerialNumber;
apwsz[5] = m_strCAName;
CSASSERT(6 <= ARRAYSIZE(apwsz));
if (0 == FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_ARGUMENT_ARRAY,
g_hInstance, // lpSource
MSG_ENROLLMENT_NOTIFICATION, // dwMessageId
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // dwLanguageId
(WCHAR *) &pwszMessage, // lpBuffer
0, // nSize
(va_list *) apwsz)) // Arguments
{
hr = myHLastError();
_JumpError(hr, error, "Exit:FormatMessage");
}
hr = CoCreateInstance(CDO::CLSID_Message,
NULL,
CLSCTX_INPROC_SERVER,
CDO::IID_IMessage,
reinterpret_cast<void**>(&pMsg));
_JumpIfError(hr, error, "CoCreateInstance CDO_IConfiguration");
hr = pMsg->putref_Configuration(m_pICDOConfig);
_JumpIfError(hr, error, "putref_Configuration");
// compose and send message
hr = pMsg->put_To(strEMail);
_JumpIfError(hr, error, "put_To");
hr = pMsg->put_From(V_BSTR(&m_varFrom));
_JumpIfError(hr, error, "put_From");
hr = pMsg->put_TextBody(pwszMessage);
_JumpIfError(hr, error, "put_Body");
// optional, could be empty
if(VT_BSTR==V_VT(&m_varCC))
{
hr = pMsg->put_CC(V_BSTR(&m_varCC));
_JumpIfError(hr, error, "put_CC");
}
// optional, could be empty
if(VT_BSTR==V_VT(&m_varSubject))
{
hr = pMsg->put_Subject(V_BSTR(&m_varSubject));
_JumpIfError(hr, error, "put_Subject");
}
hr = pMsg->Send();
_JumpIfError(hr, error, "Send");
error:
if (NULL != pwszMessage)
{
LocalFree(pwszMessage);
}
if (NULL != strEMail)
{
SysFreeString(strEMail);
}
if (NULL != strRequester)
{
SysFreeString(strRequester);
}
if (NULL != strDN)
{
SysFreeString(strDN);
}
if (NULL != strSerialNumber)
{
SysFreeString(strSerialNumber);
}
if (NULL != strCertTypeFriendlyName)
{
SysFreeString(strCertTypeFriendlyName);
}
if(pMsg)
{
pMsg->Release();
}
return(hr);
}