//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1999 - 2000
//
// File:        event.cpp
//
// Contents:    Cert CAuditEvent class implementation
//
//---------------------------------------------------------------------------
#include <pch.cpp>
#pragma hdrstop
#include <sid.h>
#include <tfc.h>
#include <authzi.h>

using namespace CertSrv;

CAuditEvent::AUDIT_CATEGORIES cat[] = 
{    // event ID                            // event category         no of   check role separation
                                                                    //args    for this event
    {SE_AUDITID_CERTSRV_SHUTDOWN,           AUDIT_FILTER_STARTSTOP,    0,   TRUE},
    {SE_AUDITID_CERTSRV_SERVICESTART,       AUDIT_FILTER_STARTSTOP,    2,   TRUE},
    {SE_AUDITID_CERTSRV_SERVICESTOP,        AUDIT_FILTER_STARTSTOP,    2,   TRUE},

    {SE_AUDITID_CERTSRV_BACKUPSTART,        AUDIT_FILTER_BACKUPRESTORE,1,   TRUE},
    {SE_AUDITID_CERTSRV_BACKUPEND,          AUDIT_FILTER_BACKUPRESTORE,0,   TRUE},
    {SE_AUDITID_CERTSRV_RESTORESTART,       AUDIT_FILTER_BACKUPRESTORE,0,   FALSE},
    {SE_AUDITID_CERTSRV_RESTOREEND,         AUDIT_FILTER_BACKUPRESTORE,0,   FALSE},

    {SE_AUDITID_CERTSRV_DENYREQUEST,        AUDIT_FILTER_CERTIFICATE,  1,   TRUE},
    {SE_AUDITID_CERTSRV_RESUBMITREQUEST,    AUDIT_FILTER_CERTIFICATE,  1,   TRUE},
    {SE_AUDITID_CERTSRV_SETEXTENSION,       AUDIT_FILTER_CERTIFICATE,  5,   TRUE},
    {SE_AUDITID_CERTSRV_SETATTRIBUTES,      AUDIT_FILTER_CERTIFICATE,  2,   TRUE},
    {SE_AUDITID_CERTSRV_IMPORTCERT,         AUDIT_FILTER_CERTIFICATE,  2,   TRUE},
    {SE_AUDITID_CERTSRV_NEWREQUEST,         AUDIT_FILTER_CERTIFICATE,  3,   FALSE},
    {SE_AUDITID_CERTSRV_REQUESTAPPROVED,    AUDIT_FILTER_CERTIFICATE,  6,   FALSE},
    {SE_AUDITID_CERTSRV_REQUESTDENIED,      AUDIT_FILTER_CERTIFICATE,  6,   FALSE},
    {SE_AUDITID_CERTSRV_REQUESTPENDING,     AUDIT_FILTER_CERTIFICATE,  6,   FALSE},
    {SE_AUDITID_CERTSRV_DELETEROW,          AUDIT_FILTER_CERTIFICATE,  3,   TRUE},
    {SE_AUDITID_CERTSRV_PUBLISHCACERT,      AUDIT_FILTER_CERTIFICATE,  3,   FALSE},

    {SE_AUDITID_CERTSRV_REVOKECERT,         AUDIT_FILTER_CERTREVOCATION, 2, TRUE},
    {SE_AUDITID_CERTSRV_PUBLISHCRL,         AUDIT_FILTER_CERTREVOCATION, 3, TRUE},
    {SE_AUDITID_CERTSRV_AUTOPUBLISHCRL,     AUDIT_FILTER_CERTREVOCATION, 5, FALSE},

    {SE_AUDITID_CERTSRV_SETSECURITY,        AUDIT_FILTER_CASECURITY,   1,   TRUE},
    {SE_AUDITID_CERTSRV_SETAUDITFILTER,     AUDIT_FILTER_CASECURITY,   1,   TRUE},
    {SE_AUDITID_CERTSRV_SETOFFICERRIGHTS,   AUDIT_FILTER_CASECURITY,   2,   TRUE},
    {SE_AUDITID_CERTSRV_ROLESEPARATIONSTATE,AUDIT_FILTER_CASECURITY,   1,   FALSE},

    {SE_AUDITID_CERTSRV_GETARCHIVEDKEY,     AUDIT_FILTER_KEYAARCHIVAL, 1,   TRUE},
    {SE_AUDITID_CERTSRV_KEYARCHIVED,        AUDIT_FILTER_KEYAARCHIVAL, 3,   FALSE},
    {SE_AUDITID_CERTSRV_IMPORTKEY,          AUDIT_FILTER_KEYAARCHIVAL, 1,   TRUE},

    {SE_AUDITID_CERTSRV_SETCONFIGENTRY,     AUDIT_FILTER_CACONFIG,     3,   TRUE},
    {SE_AUDITID_CERTSRV_SETCAPROPERTY,      AUDIT_FILTER_CACONFIG,     4,   TRUE},
};
CAuditEvent::AUDIT_CATEGORIES *CAuditEvent::m_gAuditCategories = cat;

DWORD CAuditEvent::m_gdwAuditCategoriesSize = sizeof(cat)/sizeof(cat[0]);

bool CAuditEvent::m_gfRoleSeparationEnabled = false;

CAuditEvent::CAuditEvent(ULONG ulEventID, DWORD dwFilter) :
    m_cEventData(0),
    m_cRequiredEventData(0),
    m_dwFilter(dwFilter),
    m_fRoleSeparationEnabled(false),
    m_pISS(NULL),
    m_hClientToken(NULL),
    m_pCASD(NULL),
    m_ClientContext(NULL),
    m_pSDPrivileges(NULL),
    m_pDaclPrivileges(NULL),
    m_hRpc(NULL),
    m_Error(0),
    m_MaskAllowed(0),
    m_crtGUID(0),
    m_pUserSid(NULL),
    m_hAuditEventType(NULL)
{
    m_AuthzHandle = NULL;

    m_Request.ObjectTypeList = NULL;
    m_Request.PrincipalSelfSid = NULL;
    m_Request.ObjectTypeListLength = 0;
    m_Request.OptionalArguments = NULL;

    m_Reply.ResultListLength = 1;
    m_Reply.GrantedAccessMask = &m_MaskAllowed;
    m_Reply.Error = &m_Error;
    m_Reply.SaclEvaluationResults = &m_SaclEval;

    SetEventID(ulEventID);
};

// initializes internal data associated with a particular audit event
void CAuditEvent::SetEventID(ULONG ulEventID)
{
    m_ulEventID = ulEventID;

    for(DWORD c=0; c<m_gdwAuditCategoriesSize; c++)
    {
        if(((DWORD)m_ulEventID)==((DWORD)m_gAuditCategories[c].ulAuditID))
        {
            m_fRoleSeparationEnabled = 
                m_gAuditCategories[c].fRoleSeparationEnabled;

            m_cRequiredEventData = m_gAuditCategories[c].dwParamCount;

            CSASSERT(m_EventDataMaxSize>=m_cRequiredEventData);

            if(!m_gAuditCategories[c].hAuditEventType)
            {
                AuthziInitializeAuditEventType(
                    0,
                    SE_CATEGID_OBJECT_ACCESS,
                    (USHORT)m_ulEventID,
                    (USHORT)m_gAuditCategories[c].dwParamCount,
                    &m_gAuditCategories[c].hAuditEventType);

            }
            m_hAuditEventType = m_gAuditCategories[c].hAuditEventType;
            break;
        }
    }
}

CAuditEvent::~CAuditEvent()
{
    for(DWORD cData=0;cData<m_cEventData;cData++)
        delete m_pEventDataList[cData];

    FreeCachedHandles();
}

void CAuditEvent::CleanupAuditEventTypeHandles()
{
    for(DWORD c=0; c<m_gdwAuditCategoriesSize; c++)
    {
        if(m_gAuditCategories[c].hAuditEventType)
        {
            AuthziFreeAuditEventType(m_gAuditCategories[c].hAuditEventType);
        }
    }
}

bool CAuditEvent::IsEventValid()
{
    for(DWORD c=0; c<m_gdwAuditCategoriesSize; c++)
    {
        if(m_ulEventID==m_gAuditCategories[c].ulAuditID)
            return true;
    }
    return false;
}

bool CAuditEvent::IsEventEnabled()
{
    if(0==m_ulEventID) // event is used for access check only
        return false;

    for(DWORD c=0; c<m_gdwAuditCategoriesSize; c++)
    {
        if(((DWORD)m_ulEventID)==((DWORD)m_gAuditCategories[c].ulAuditID))
        {
            return (m_dwFilter&m_gAuditCategories[c].dwFilter)?true:false;
        }
    }
    // if get here the event has an unknown ID
    CSASSERT(!L"Invalid event found");
    return false;
}

inline bool CAuditEvent::IsEventRoleSeparationEnabled()
{
    return RoleSeparationIsEnabled() && m_fRoleSeparationEnabled;
}

HRESULT CAuditEvent::AddData(DWORD dwValue)
{
    PROPVARIANT *pvtData = CreateNewEventData();
    if(!pvtData)
    {
        return E_OUTOFMEMORY;
    }
    V_VT(pvtData) = VT_UI4;
    V_UI4(pvtData) = dwValue;
    return S_OK;
}

HRESULT CAuditEvent::AddData(PBYTE pData, DWORD dwDataLen)
{
    CSASSERT(pData && dwDataLen);

    PROPVARIANT *pvtData = CreateNewEventData();
    if(!pvtData)
    {
        return E_OUTOFMEMORY;
    }
    V_VT(pvtData) = VT_BLOB;
    pvtData->blob.cbSize = dwDataLen;
    pvtData->blob.pBlobData = (BYTE*)CoTaskMemAlloc(dwDataLen);
    if(!pvtData->blob.pBlobData)
    {
        return E_OUTOFMEMORY;
    }
    memcpy(pvtData->blob.pBlobData, pData, dwDataLen);
    return S_OK;
}

HRESULT CAuditEvent::AddData(bool fData)
{
    PROPVARIANT *pvtData = CreateNewEventData();
    if(!pvtData)
    {
        return E_OUTOFMEMORY;
    }
    V_VT(pvtData) = VT_BOOL;
    V_BOOL(pvtData) = fData?VARIANT_TRUE:VARIANT_FALSE;
    return S_OK;
}

HRESULT CAuditEvent::AddData(LPCWSTR pcwszData)
{
    if(!pcwszData)
        pcwszData = L"";

    PROPVARIANT *pvtData = CreateNewEventData();
    if(!pvtData)
    {
        return E_OUTOFMEMORY;
    }
    V_VT(pvtData) = VT_LPWSTR;
    pvtData->pwszVal = 
        (LPWSTR)CoTaskMemAlloc((wcslen(pcwszData)+1)*sizeof(WCHAR));
    if(!pvtData->pwszVal)
    {
        return E_OUTOFMEMORY;
    }
    wcscpy(pvtData->pwszVal, pcwszData);
    return S_OK;
}

HRESULT CAuditEvent::AddData(LPCWSTR *ppcwszData)
{
    CSASSERT(ppcwszData);

    PROPVARIANT *pvtData = CreateNewEventData();
    if(!pvtData)
    {
        return E_OUTOFMEMORY;
    }
    V_VT(pvtData) = VT_LPWSTR;

    DWORD dwTextLen = 1;

    for(LPCWSTR *ppcwszStr=ppcwszData; *ppcwszStr; ppcwszStr++)
    {
        dwTextLen += wcslen(*ppcwszStr)+2;
    }
    
    pvtData->pwszVal = 
        (LPWSTR)CoTaskMemAlloc(dwTextLen*sizeof(WCHAR));
    if(!pvtData->pwszVal)
    {
        return E_OUTOFMEMORY;
    }
    wcscpy(pvtData->pwszVal, L"");
    for(ppcwszStr=ppcwszData;  *ppcwszStr; ppcwszStr++)
    {
        wcscat(pvtData->pwszVal, *ppcwszStr);
        wcscat(pvtData->pwszVal, L"; ");
    }
    return S_OK;
}

HRESULT CAuditEvent::AddData(FILETIME time)
{
    PROPVARIANT *pvtData = CreateNewEventData();
    if(!pvtData)
    {
        return E_OUTOFMEMORY;
    }
    V_VT(pvtData) = VT_FILETIME;
    pvtData->filetime = time;
    return S_OK;
}

HRESULT CAuditEvent::AddData(const VARIANT *pvar, bool fDoublePercentInStrings=false)
{
    CSASSERT(pvar);

    EventData *pData = CreateNewEventData1();
    if(!pData)
    {
        return E_OUTOFMEMORY;
    }
    pData->m_fDoublePercentsInStrings = fDoublePercentInStrings;

    HRESULT hr = VariantCopy((VARIANT*)&pData->m_vtData, (VARIANT*)pvar);

    return hr;
}


PROPVARIANT *CAuditEvent::CreateNewEventData()
{
    EventData *pData = CreateNewEventData1();
    return &pData->m_vtData;
}

CAuditEvent::EventData *CAuditEvent::CreateNewEventData1()
{
    EventData *pData = new EventData;
    if(!pData)
    {
        return NULL;
    }
    m_pEventDataList[m_cEventData++] = pData;
    return pData;
}

HRESULT CAuditEvent::EventData::ConvertToStringI2I4(
    LONG lVal,
    LPWSTR *ppwszOut)
{
    WCHAR wszVal[100]; // big enough to hold a LONG as string
    _itow(lVal, wszVal, 10);
    *ppwszOut = new WCHAR[wcslen(wszVal)+1];
    if(!*ppwszOut)
    {
        return E_OUTOFMEMORY;
    }
    wcscpy(*ppwszOut, wszVal);
    return S_OK;
}

HRESULT CAuditEvent::EventData::ConvertToStringUI2UI4(
    ULONG ulVal,
    LPWSTR *ppwszOut)
{
    WCHAR wszVal[100]; // big enough to hold a LONG as string
    _itow(ulVal, wszVal, 10);
    *ppwszOut = new WCHAR[wcslen(wszVal)+1];
    if(!*ppwszOut)
    {
        return E_OUTOFMEMORY;
    }
    wcscpy(*ppwszOut, wszVal);
    return S_OK;
}

HRESULT CAuditEvent::EventData::DoublePercentsInString(
    LPCWSTR pcwszIn,
    LPWSTR *ppwszOut)
{
    const WCHAR *pchSrc;
    WCHAR *pchDest;
    int cPercentChars = 0;

    wprintf(L"********Found %d percents\n", cPercentChars);

    *ppwszOut = new WCHAR[2*wcslen(pcwszIn)+1];
    if(!*ppwszOut)
        return E_OUTOFMEMORY;

    for(pchSrc = pcwszIn, pchDest = *ppwszOut; 
        L'\0'!=*pchSrc; 
        pchSrc++, pchDest++)
    {
        *pchDest = *pchSrc;
        if(L'%'==*pchSrc)
            *(++pchDest) = L'%';
    }
    *pchDest = L'\0';

    wprintf(L"****************************");
    wprintf(L"%s%", *ppwszOut);
    wprintf(L"****************************");

    return S_OK;
}


HRESULT CAuditEvent::EventData::ConvertToStringWSZ(
    LPCWSTR pcwszVal,
    LPWSTR *ppwszOut)

{
    if(m_fDoublePercentsInStrings)
    {
        // replace each occurence of % with %%
        return DoublePercentsInString(
            pcwszVal,
            ppwszOut);
    }
    else
    {
        *ppwszOut = new WCHAR[(wcslen(pcwszVal)+1)];
        if(!*ppwszOut)
        {
            return E_OUTOFMEMORY;
        }
        wcscpy(*ppwszOut, pcwszVal);
    }
    return S_OK;
}

HRESULT CAuditEvent::EventData::ConvertToStringBOOL(
    BOOL fVal,
    LPWSTR *ppwszOut)

{
    LPCWSTR pwszBoolVal = 
        fVal==VARIANT_TRUE?
        g_pwszYes:
        g_pwszNo;
    *ppwszOut = new WCHAR[wcslen(pwszBoolVal)+1];
    if(!*ppwszOut)
    {
        return E_OUTOFMEMORY;
    }
    wcscpy(*ppwszOut, pwszBoolVal);
    return S_OK;
}


HRESULT CAuditEvent::EventData::ConvertToStringArrayUI1(
    LPSAFEARRAY psa,
    LPWSTR *ppwszOut)
{
    SafeArrayEnum<BYTE> saenum(psa);
    if(!saenum.IsValid())
    {
        return E_INVALIDARG;
    }
    BYTE b;
    // byte array is formated as "0x00 0x00..." ie 5 
    // chars per byte
    *ppwszOut = new WCHAR[saenum.GetCount()*5];
    if(!*ppwszOut)
        return E_OUTOFMEMORY;

    LPWSTR pwszCrt = *ppwszOut;
    while(S_OK==saenum.Next(b))
    {
        wsprintf(pwszCrt, L"0x%02X ", b); // eg "0x0f" or "0xa4"
        pwszCrt+=5;
    }
    return S_OK;
}

HRESULT CAuditEvent::EventData::ConvertToStringArrayBSTR(
    LPSAFEARRAY psa,
    LPWSTR *ppwszOut)
{
    SafeArrayEnum<BSTR> saenum(psa);
    if(!saenum.IsValid())
    {
        return E_INVALIDARG;
    }
    DWORD dwLen = 1;
    BSTR bstr;

    while(S_OK==saenum.Next(bstr))
    {
        dwLen+=2*wcslen(bstr)+10;
    }
    *ppwszOut = new WCHAR[dwLen];
    if(!*ppwszOut)
        return E_OUTOFMEMORY;
    **ppwszOut = L'\0';
        
    saenum.Reset();

    while(S_OK==saenum.Next(bstr))
    {
        if(m_fDoublePercentsInStrings)
        {
            CAutoLPWSTR pwszTemp;
            if(S_OK != DoublePercentsInString(bstr,
                &pwszTemp))
            {
                delete[] *ppwszOut;
                *ppwszOut = NULL;
                return E_OUTOFMEMORY;
            }
            wcscat(*ppwszOut, pwszTemp);
        }
        else
        {
            wcscat(*ppwszOut, bstr);
        }
        wcscat(*ppwszOut, L"\n");
    }
    return S_OK;
}

HRESULT CAuditEvent::EventData::ConvertToString(LPWSTR *ppwszValue)
{
    LPWSTR pwszVal = NULL;
    HRESULT hr = S_OK;
    switch(V_VT(&m_vtData))
    {
    case VT_I2:
        hr = ConvertToStringI2I4(V_I2(&m_vtData), ppwszValue);
        break;
    case VT_BYREF|VT_I2:
        hr = ConvertToStringI2I4(*V_I2REF(&m_vtData), ppwszValue);
        break;
    case VT_I4:
        hr = ConvertToStringI2I4(V_I4(&m_vtData), ppwszValue);
        break;
    case VT_BYREF|VT_I4:
        hr = ConvertToStringI2I4(*V_I4REF(&m_vtData), ppwszValue);
        break;
    case VT_UI2:
        hr = ConvertToStringUI2UI4(V_UI2(&m_vtData), ppwszValue);
        break;
    case VT_BYREF|VT_UI2:
        hr = ConvertToStringUI2UI4(*V_UI2REF(&m_vtData), ppwszValue);
        break;
    case VT_UI4:
        hr = ConvertToStringUI2UI4(V_UI4(&m_vtData), ppwszValue);
        break;
    case VT_BYREF|VT_UI4:
        hr = ConvertToStringUI2UI4(*V_UI4REF(&m_vtData), ppwszValue);
        break;

    case VT_BLOB:
	// We don't call CryptBinaryToString directly anywhere in the CA tree.
	// This avoids errors when linking in the CryptBinaryToString code
	// for NT 4 reskit builds.

        WCHAR *pwszLocalAlloc;

        hr = myCryptBinaryToString(
                m_vtData.blob.pBlobData,
                m_vtData.blob.cbSize,
                CRYPT_STRING_BASE64,
                &pwszLocalAlloc);
        if (S_OK != hr)
        {
            return(hr);
        }
        pwszVal = new WCHAR[(wcslen(pwszLocalAlloc) + 1)];
        if (NULL == pwszVal)
        {
            LocalFree(pwszLocalAlloc);
            return E_OUTOFMEMORY;
        }
        wcscpy(pwszVal, pwszLocalAlloc);
        LocalFree(pwszLocalAlloc);
        *ppwszValue = pwszVal;
//        pwszVal[cOut-2] = L'\0'; \\ Base64Encode adds a new line
        break;

    case VT_BOOL:
        hr = ConvertToStringBOOL(V_BOOL(&m_vtData), ppwszValue);
        break;
    case VT_BOOL|VT_BYREF:
        hr = ConvertToStringBOOL(*V_BOOLREF(&m_vtData), ppwszValue);
        break;

    case VT_LPWSTR:
        hr = ConvertToStringWSZ(m_vtData.pwszVal, ppwszValue);
        break;
    case VT_BSTR:
        hr = ConvertToStringWSZ(V_BSTR(&m_vtData), ppwszValue);
        break;
    case VT_BSTR|VT_BYREF:
        hr = ConvertToStringWSZ(*V_BSTRREF(&m_vtData), ppwszValue);
        break;

    case VT_FILETIME:
        {
        LPWSTR pwszTime = NULL;
        hr = myFileTimeToWszTime(
                &m_vtData.filetime,
                TRUE,
                &pwszTime);
        if(FAILED(hr))
            return hr;
        pwszVal = new WCHAR[wcslen(pwszTime)+1];
        if(!pwszVal)
        {
            return E_OUTOFMEMORY;
        }
        wcscpy(pwszVal, pwszTime);
        LocalFree(pwszTime);
        *ppwszValue = pwszVal;
        }
        break;

    case VT_ARRAY|VT_UI1:
        hr = ConvertToStringArrayUI1(V_ARRAY(&m_vtData), ppwszValue);
        break;
    case VT_ARRAY|VT_UI1|VT_BYREF:
        hr = ConvertToStringArrayUI1(*V_ARRAYREF(&m_vtData), ppwszValue);
        break;
    case VT_ARRAY|VT_BSTR:
        hr = ConvertToStringArrayBSTR(V_ARRAY(&m_vtData), ppwszValue);
        break;
    case VT_ARRAY|VT_BSTR|VT_BYREF:
        hr = ConvertToStringArrayBSTR(*V_ARRAYREF(&m_vtData), ppwszValue);
        break;

    default:
        {
            LPCWSTR pwszValOut = cAuditString_UnknownDataType;
            VARIANT varOut;
            VariantInit(&varOut);

            hr = VariantChangeType(&varOut, (VARIANT*)&m_vtData, 0, VT_BSTR);
            if(S_OK==hr)
            {
                pwszValOut = V_BSTR(&varOut);
            }
            pwszVal = new WCHAR[wcslen(pwszValOut)+1];
            if(!pwszVal)
            {
                return E_OUTOFMEMORY;
            }
            wcscpy(pwszVal, pwszValOut);
            VariantClear(&varOut);
            *ppwszValue = pwszVal;
            hr = S_OK;
        }
        break;
    }
    
    return hr;
}

HRESULT CAuditEvent::Report(bool fSuccess /* = true */)
{
    HRESULT hr;
    AUTHZ_AUDIT_EVENT_HANDLE AuthzAIH = NULL;
    PAUDIT_PARAMS pAuditParams = NULL;
    PAUDIT_PARAM pParamArray = NULL;

    if(!IsEventEnabled())
    {
        return S_OK;
    }
    hr = BuildAuditParamArray(pParamArray);
    _JumpIfError(hr, error, "GetAuditText");

    if(!AuthziAllocateAuditParams(
        &pAuditParams, 
        (USHORT)(m_cEventData+2))) // authz adds 2 
    {                                                             // extra params
        hr = myHLastError();                                      // internally
        _JumpError(hr, error, "AuthziAllocateAuditParams");
    }

#ifndef _DISABLE_AUTHZ_
    if(!AuthziInitializeAuditParamsFromArray(
            fSuccess?APF_AuditSuccess:APF_AuditFailure,
            g_AuthzCertSrvRM,
            (USHORT)m_cEventData,
            pParamArray,
            pAuditParams))
#else
    SetLastError(E_INVALIDARG);
#endif
    {
        hr = myHLastError();
        _JumpError(hr, error, "AuthziInitializeAuditParamsFromArray");
    }

    if (!AuthziInitializeAuditEvent(0,
                                   g_AuthzCertSrvRM,
                                   m_hAuditEventType,
                                   pAuditParams,
                                   NULL,
                                   INFINITE,
                                   L"",
                                   L"",
                                   L"",
                                   L"",
                                   &AuthzAIH))

    {
        hr = myHLastError();
        _JumpIfError(hr, error, "AuthzInitializeAuditInfo");
    }

   if(!AuthziLogAuditEvent( 0, AuthzAIH, NULL ))
   {
       hr = myHLastError();
       _JumpIfError(hr, error, "AuthzGenAuditEvent");
   }

    DBGPRINT((
	DBG_SS_AUDIT,
	"Audit event ID=%d\n",
	m_ulEventID));

error:

    if(AuthzAIH)
    {
        AuthzFreeAuditEvent(AuthzAIH);
    }

    if(pAuditParams)
    {
        AuthziFreeAuditParams(pAuditParams);
    }

    FreeAuditParamArray(pParamArray);

    return hr;
}

HRESULT CAuditEvent::SaveFilter(LPCWSTR pcwszSanitizedName)
{
    return mySetCertRegDWValue(
			    pcwszSanitizedName,
			    NULL,
			    NULL,
			    wszREGAUDITFILTER,
                m_dwFilter);
}

HRESULT CAuditEvent::LoadFilter(LPCWSTR pcwszSanitizedName)
{
    return myGetCertRegDWValue(
			    pcwszSanitizedName,
			    NULL,
			    NULL,
			    wszREGAUDITFILTER,
                &m_dwFilter);
}

HRESULT CAuditEvent::Impersonate()
{
    HRESULT hr;
    HANDLE hThread = NULL;

    CSASSERT(NULL==m_pISS);
    CSASSERT(NULL==m_hClientToken);
    
    if (NULL == m_hRpc)
    {
        // dcom impersonate
        hr = CoGetCallContext(IID_IServerSecurity, (void**)&m_pISS);
        _JumpIfError(hr, error, "CoGetCallContext");

        hr = m_pISS->ImpersonateClient();
        _JumpIfError(hr, error, "ImpersonateClient");
    }
    else
    {
        // rpc impersonate
        hr = RpcImpersonateClient((RPC_BINDING_HANDLE) m_hRpc);
        if (S_OK != hr)
        {
            hr = myHError(hr);
            _JumpError(hr, error, "RpcImpersonateClient");
        }
    }

    hThread = GetCurrentThread();
    if (NULL == hThread)
    {
        hr = myHLastError();
        _JumpIfError(hr, error, "GetCurrentThread");
    }
    if (!OpenThreadToken(hThread,
                         TOKEN_QUERY | TOKEN_DUPLICATE,
                         FALSE,  // client impersonation
                         &m_hClientToken))
    {
        hr = myHLastError();
        _JumpIfError(hr, error, "OpenThreadToken");
    }

error:

    if(S_OK!=hr)
    {
        if(NULL!=m_pISS)
        {
            m_pISS->Release();
            m_pISS = NULL;
        }
    }
    if (NULL != hThread)
    {
        CloseHandle(hThread);
    }
    return hr;
}

HRESULT CAuditEvent::RevertToSelf()
{
    HRESULT hr = S_OK;
//    CSASSERT(m_pISS||m_hRpc);

    if (NULL != m_hRpc) // rpc
    {
        hr = RpcRevertToSelf();
        if (S_OK != hr)
        {
            hr = myHError(hr);
            _JumpError(hr, error, "RpcRevertToSelf");
        }
        m_hRpc = NULL;
    }
    else  if(m_pISS) // dcom
    {
        hr = m_pISS->RevertToSelf();
        _JumpIfError(hr, error, "IServerSecurity::RpcRevertToSelf");

        m_pISS->Release();
        m_pISS = NULL;
    }

error:
    return hr;
}

HANDLE CAuditEvent::GetClientToken()
{
    CSASSERT(m_hClientToken);
    HANDLE hSave = m_hClientToken;
    m_hClientToken = NULL;
    return hSave;
}

// dwAuditFlags - not asking for both success and failure implicitely
// means the handles will be cached for future audit
HRESULT
CAuditEvent::AccessCheck(
    ACCESS_MASK Mask,
    DWORD dwAuditFlags,
    handle_t hRpc,
    HANDLE *phToken)
{
    HRESULT hr = S_OK;
    LUID luid = {0,0};
    bool fAccessAllowed = false;
    DWORD dwRoles = 0;
    DWORD dwRolesChecked = 0;
    PACL pDacl = NULL;

    FreeCachedHandles();

    m_hRpc = hRpc;

    if (!g_CASD.IsInitialized())
    {
        hr = HRESULT_FROM_WIN32(ERROR_NOT_READY);
        _JumpError(hr, error, "Security not enabled");
    }
    
    hr = g_CASD.LockGet(&m_pCASD);
    _JumpIfError(hr, error, "CProtectedSecurityDescriptor::LockGet");

    hr = Impersonate();
    _JumpIfError(hr, error, "CAuditEvent::Impersonate");

    if(!AuthzInitializeContextFromToken(
            0,
            m_hClientToken,
            g_AuthzCertSrvRM,
            NULL,
            luid,
            NULL,
            &m_ClientContext))
    {
        hr = myHLastError();
        _PrintError(hr, "AuthzInitializeContextFromToken");
    
        if (E_INVALIDARG == hr && !IsWhistler())
        {
            hr = S_OK;
            fAccessAllowed = TRUE;
        }
        goto error;
    }

    if(Mask & CA_ACCESS_LOCALADMIN)
    {
        bool fLocalAdmin;

        hr = IsCurrentUserBuiltinAdmin(&fLocalAdmin);
        _JumpIfError(hr, error, "IsCurrentUserBuiltinAdmin");

        if(fLocalAdmin)
        {
            dwRoles |= CA_ACCESS_LOCALADMIN;
        }
    }

    RevertToSelf();

    // Get privilege based roles if checking access on a privilege role
    // or if role separation is enabled when we have to know all roles
    if(IsEventRoleSeparationEnabled() ||
       Mask & (CA_ACCESS_OPERATOR|CA_ACCESS_AUDITOR|CA_ACCESS_LOCALADMIN))
    {
        hr = GetPrivilegeRoles(&dwRoles);
        _JumpIfError(hr, error, "CAuditEvent::GetPrivilegeRolesCount");

        hr = BuildPrivilegeSecurityDescriptor(dwRoles);
        _JumpIfError(hr, error, "CAuditEvent::BuildPrivilegeSecurityDescriptor");
    }

    // Get security descriptor based roles
    m_Request.DesiredAccess = MAXIMUM_ALLOWED;
    CSASSERT(!m_AuthzHandle);
    if(!AuthzAccessCheck(
            0,
            m_ClientContext,
            &m_Request,
            NULL, //no audit
            m_pCASD,
            m_pSDPrivileges?(&m_pSDPrivileges):NULL,
            m_pSDPrivileges?1:0,
            &m_Reply,
            IsEventEnabled()?&m_AuthzHandle:NULL)) // no caching if no audit
                                                   // event will be generated
    {
        hr = myHLastError();
        _JumpError(hr, error, "AuthzAccessCheck");
    }
    
    dwRoles |= m_Reply.GrantedAccessMask[0];
    
    if(m_Reply.Error[0]==ERROR_SUCCESS &&
       m_Reply.GrantedAccessMask[0]&Mask)
    {
        fAccessAllowed = true;
    }

    if(IsEventRoleSeparationEnabled() &&
       GetBitCount(dwRoles&CA_ACCESS_MASKROLES)>1)
    {
        hr = CERTSRV_E_ROLECONFLICT;
        fAccessAllowed = false;
        // don't return yet, we need to generate an audit
    }

    // Next is a fake access check to generate an audit. 
    // Access is denied if:
    // - role separation is enabled and user has more than one role
    // - none of the roles requested is allowed

    // Generate audit if event is enabled and 
    if(IsEventEnabled() &&
        (!fAccessAllowed && !(dwAuditFlags&m_gcNoAuditFailure) ||
         fAccessAllowed && !(dwAuditFlags&m_gcNoAuditSuccess)))
    {

        m_Request.DesiredAccess = 
            fAccessAllowed?
            m_Reply.GrantedAccessMask[0]&Mask:
            Mask;

        if(CERTSRV_E_ROLECONFLICT==hr)
            m_Request.DesiredAccess = 0x0000ffff; //force a failure audit
        
        HRESULT hr2 = CachedGenerateAudit();
        if(S_OK != hr2)
        {
            hr = hr2;
            _JumpIfError(hr, error, "CAuditEvent::CachedGenerateAudit");
        }
    }

    if(phToken)
    {
        *phToken = GetClientToken();
    }

error:

#ifdef DBG_CERTSRV_DEBUG_PRINT
    if(IsEventRoleSeparationEnabled())
    {
        DBGPRINT((DBG_SS_AUDIT, "EVENT %d ROLES: 0x%x %s%s%s%s%s%s\n", 
            m_ulEventID,
            dwRoles,
        (dwRoles&CA_ACCESS_ADMIN)?"CAADMIN ":"",
        (dwRoles&CA_ACCESS_OFFICER)?"OFFICER ":"",
        (dwRoles&CA_ACCESS_AUDITOR)?"AUDITOR ":"",
        (dwRoles&CA_ACCESS_OPERATOR)?"OPERATOR ":"",
        (dwRoles&CA_ACCESS_ENROLL)?"ENROLL ":"",
        (dwRoles&CA_ACCESS_READ)?"READ ":""));
    }
#endif

    if(!IsEventEnabled())
    {
        FreeCachedHandles();
    }

    if(S_OK==hr)
    {
        hr = fAccessAllowed?S_OK:E_ACCESSDENIED;
    }

    return(hr);
}

HRESULT 
CAuditEvent::CachedGenerateAudit()
{
    HRESULT hr = S_OK;
    AUTHZ_AUDIT_EVENT_HANDLE AuditInfo = NULL;
    PAUDIT_PARAMS pAuditParams = NULL;
    PAUDIT_PARAM pParamArray = NULL;

    if(!IsEventEnabled())
    {
        FreeCachedHandles();
        return S_OK;
    }

    CSASSERT(m_AuthzHandle);

    hr = BuildAuditParamArray(pParamArray);
    _JumpIfError(hr, error, "GetAuditText");

    if(!AuthziAllocateAuditParams(
        &pAuditParams, 
        (USHORT)(m_cEventData+2))) // authz adds 2 
    {                                                             // extra params
        hr = myHLastError();                                      // internally
        _JumpError(hr, error, "AuthziAllocateAuditParams");
    }

#ifndef _DISABLE_AUTHZ_
    if(!AuthziInitializeAuditParamsFromArray(
            APF_AuditSuccess|APF_AuditFailure,
            g_AuthzCertSrvRM,
            (USHORT)m_cEventData,
            pParamArray,
            pAuditParams))
#else
    SetLastError(E_INVALIDARG);
#endif
    {
        hr = myHLastError();
        _JumpError(hr, error, "AuthzInitAuditParams");
    }

    if(!AuthziInitializeAuditEvent(
            0,
            g_AuthzCertSrvRM,
            m_hAuditEventType,
            pAuditParams,
            NULL,
            INFINITE,
            L"",
            L"",
            L"",
            L"",
            &AuditInfo))
    {
        hr = myHLastError();
        _JumpError(hr, error, "AuthzInitAuditInfoHandle");
    }

    if(!AuthzCachedAccessCheck(
            0,
            m_AuthzHandle,
            &m_Request,
            AuditInfo,
            &m_Reply))
    {
        hr = myHLastError();
        _JumpError(hr, error, "AuthzCachedAccessCheck");
    }

error:

    if(AuditInfo)
    {
        AuthzFreeAuditEvent(AuditInfo);
    }

    if(pAuditParams)
    {
        AuthziFreeAuditParams(pAuditParams);
    }

    FreeCachedHandles();
    FreeAuditParamArray(pParamArray);

    return hr;
}


void CAuditEvent::FreeCachedHandles()
{
    if(m_hClientToken)
    {
        CloseHandle(m_hClientToken);
        m_hClientToken = NULL;
    }

    if(m_AuthzHandle)
    {
        AuthzFreeHandle(m_AuthzHandle);
        m_AuthzHandle = NULL;
    }

    if(m_pCASD)
    {
        g_CASD.Unlock();
        m_pCASD = NULL;
    }

    if(m_ClientContext)
    {
        AuthzFreeContext(m_ClientContext);
        m_ClientContext = NULL;
    }

    if(m_pUserSid)
    {
        LocalFree(m_pUserSid);
        m_pUserSid = NULL;
    }
    if(m_pSDPrivileges)
    {
        LocalFree(m_pSDPrivileges);
        m_pSDPrivileges = NULL;
    }
    if(m_pDaclPrivileges)
    {
        LocalFree(m_pDaclPrivileges);
        m_pDaclPrivileges = NULL;
    }
}

HRESULT CAuditEvent::RoleSeparationFlagSave(LPCWSTR pcwszSanitizedName)
{
    return mySetCertRegDWValue(
			    pcwszSanitizedName,
			    NULL,
			    NULL,
			    wszREGROLESEPARATIONENABLED,
                RoleSeparationIsEnabled()?1:0);
}

HRESULT CAuditEvent::RoleSeparationFlagLoad(LPCWSTR pcwszSanitizedName)
{
    DWORD dwFlags = 0;
    HRESULT hr = myGetCertRegDWValue(
			        pcwszSanitizedName,
			        NULL,
			        NULL,
			        wszREGROLESEPARATIONENABLED,
                    &dwFlags);
    if(S_OK==hr)
    {
        RoleSeparationEnable(dwFlags?true:false);
    }

    return hr;
}

HRESULT CAuditEvent::GetPrivilegeRoles(PDWORD pdwRoles)
{
    HRESULT hr = S_OK;
    PTOKEN_USER pTokenUser = NULL;
    DWORD cbTokenUser = 0;
    PTOKEN_GROUPS pTokenGroups = NULL;
    DWORD cbTokenGroups = 0;
    DWORD dwRoles = 0;
    LSA_HANDLE lsahPolicyHandle = NULL;
    LSA_OBJECT_ATTRIBUTES ObjectAttributes;
    NTSTATUS NTStatus;

    // first get roles for the user itself
    AuthzGetInformationFromContext(
            m_ClientContext,
            AuthzContextInfoUserSid,
            0,
            &cbTokenUser,
            NULL);       
     
    if(GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
    {
        hr = myHLastError();
        _JumpError(hr, error, "AuthzGetContextInformation");
    }

    pTokenUser = (PTOKEN_USER)LocalAlloc(LMEM_FIXED, cbTokenUser);
    _JumpIfAllocFailed(pTokenUser, error);

    if(!AuthzGetInformationFromContext(
            m_ClientContext,
            AuthzContextInfoUserSid,
            cbTokenUser,
            &cbTokenUser,
            pTokenUser))
    {
        hr = myHLastError();
        _JumpError(hr, error, "AuthzGetContextInformation");
    }

    // Object attributes are reserved, so initalize to zeroes.
    ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));

    NTStatus = LsaOpenPolicy(
                NULL,
                &ObjectAttributes,
                POLICY_LOOKUP_NAMES,
                &lsahPolicyHandle);
    if(STATUS_SUCCESS!=NTStatus)
    {
        hr = HRESULT_FROM_WIN32(LsaNtStatusToWinError(NTStatus));
        _JumpError(hr, error, "LsaOpenPolicy");
    }
  
    CSASSERT(!m_pUserSid);
    m_pUserSid = (PSID)LocalAlloc(LMEM_FIXED, GetLengthSid(pTokenUser->User.Sid));
    _JumpIfAllocFailed(m_pUserSid, error);

    CopySid(
        GetLengthSid(pTokenUser->User.Sid), 
        m_pUserSid,
        pTokenUser->User.Sid);

    hr = GetUserPrivilegeRoles(lsahPolicyHandle, &pTokenUser->User, &dwRoles);
    if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr)
    {
        hr =S_OK;
    }
    _JumpIfError(hr, error, "CAuditEvent::GetUserPrivilegeRoles");

    *pdwRoles |= dwRoles;

    // then find the roles assigned to the groups the user is member of

    AuthzGetInformationFromContext(
            m_ClientContext,
            AuthzContextInfoGroupsSids,
            0,
            &cbTokenGroups,
            NULL);

    if(GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
    {
        hr = myHLastError();
        _JumpError(hr, error, "AuthzGetContextInformation");
    }

    pTokenGroups = (PTOKEN_GROUPS)LocalAlloc(LMEM_FIXED, cbTokenGroups);
    _JumpIfAllocFailed(pTokenGroups, error);

    if(!AuthzGetInformationFromContext(
            m_ClientContext,
            AuthzContextInfoGroupsSids,
            cbTokenGroups,
            &cbTokenGroups,
            pTokenGroups))
    {
        hr = myHLastError();
        _JumpError(hr, error, "AuthzGetContextInformation");
    }

    for(DWORD cGroups = 0; cGroups<pTokenGroups->GroupCount; cGroups++)
    {
        dwRoles = 0;
        hr = GetUserPrivilegeRoles(
                lsahPolicyHandle,
                &pTokenGroups->Groups[cGroups], 
                &dwRoles);
        if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr)
        {
            hr =S_OK;
        }
        _JumpIfError(hr, error, "CAuditEvent::GetUserPrivilegeRoles");

        *pdwRoles |= dwRoles;
    }

error:
    if(pTokenUser)
    {
        LocalFree(pTokenUser);
    }
    if(pTokenGroups)
    {
        LocalFree(pTokenGroups);
    }
    if(lsahPolicyHandle)
    {
        LsaClose(lsahPolicyHandle);
    }
    return hr;
}


HRESULT CAuditEvent::GetUserPrivilegeRoles(
    LSA_HANDLE lsah,
    PSID_AND_ATTRIBUTES pSA, 
    PDWORD pdwRoles)
{
    NTSTATUS NTStatus;
    PLSA_UNICODE_STRING pLSAString = NULL;
    ULONG cRights, c;
    
    NTStatus = LsaEnumerateAccountRights(
                lsah,
                pSA->Sid,
                &pLSAString,
                &cRights);

    if(STATUS_SUCCESS!=NTStatus)
    {
        return HRESULT_FROM_WIN32(LsaNtStatusToWinError(NTStatus));
    }

    for(c=0; c<cRights; c++)
    {
        if(0==_wcsicmp(SE_SECURITY_NAME, pLSAString[c].Buffer))
        {
            *pdwRoles |= CA_ACCESS_AUDITOR;
        }
        else if(0==_wcsicmp(SE_BACKUP_NAME, pLSAString[c].Buffer))
        {
            *pdwRoles |= CA_ACCESS_OPERATOR;
        }
    }

    if(pLSAString)
    {
        LsaFreeMemory(pLSAString);
    }

    return S_OK;
}

HRESULT
CAuditEvent::GetMyRoles(
    DWORD *pdwRoles)
{
    HRESULT hr = S_OK;
    LUID luid = {0,0};
    bool fAccessAllowed = true;
    DWORD dwRoles = 0;

    if (!g_CASD.IsInitialized())
    {
        hr = HRESULT_FROM_WIN32(ERROR_NOT_READY);
        _JumpError(hr, error, "Security not enabled");
    }
    
    hr = g_CASD.LockGet(&m_pCASD);
    _JumpIfError(hr, error, "CProtectedSecurityDescriptor::LockGet");

    hr = Impersonate();
    _JumpIfError(hr, error, "CAuditEvent::Impersonate");

    if(!AuthzInitializeContextFromToken(
            0,
            m_hClientToken,
            g_AuthzCertSrvRM,
            NULL,
            luid,
            NULL,
            &m_ClientContext))
    {
        hr = myHLastError();
        _JumpError(hr, error, "AuthzInitializeContextFromToken");
    }

    RevertToSelf();

    hr = GetPrivilegeRoles(&dwRoles);
    _JumpIfError(hr, error, "CAuditEvent::GetPrivilegeRoles");

    m_Request.DesiredAccess = MAXIMUM_ALLOWED;
    m_Reply.GrantedAccessMask[0] = 0;

    if(!AuthzAccessCheck(
            0,
            m_ClientContext,
            &m_Request,
            NULL, //no audit
            m_pCASD,
            NULL,
            0,
            &m_Reply,
            NULL))
    {
        hr = myHLastError();
        _JumpError(hr, error, "AuthzAccessCheck");
    }
    
    dwRoles |= (m_Reply.GrantedAccessMask[0] &
                (CA_ACCESS_MASKROLES | // returned mask could also
                 CA_ACCESS_READ |      // include generic rights (like
                 CA_ACCESS_ENROLL));   // read and write DACL) which
                                       // we are not interested in
    *pdwRoles = dwRoles;

error:

    FreeCachedHandles();

    return(hr);
}


// Build a one ace DACL security descriptor with the roles
// passed in
HRESULT CAuditEvent::BuildPrivilegeSecurityDescriptor(DWORD dwRoles)
{
    HRESULT hr = S_OK;
    DWORD dwDaclSize;
    PSID pOwnerSid = NULL; // no free
    PSID pGroupSid = NULL; // no free
    BOOL fDefaulted;

    CSASSERT(NULL == m_pSDPrivileges);
    CSASSERT(NULL == m_pDaclPrivileges);

    m_pSDPrivileges = (PSECURITY_DESCRIPTOR)LocalAlloc(
                        LMEM_FIXED,
                        SECURITY_DESCRIPTOR_MIN_LENGTH);
    _JumpIfAllocFailed(m_pSDPrivileges, error);

    if (!InitializeSecurityDescriptor(
            m_pSDPrivileges,
            SECURITY_DESCRIPTOR_REVISION))
    {
        hr = myHLastError();
        _JumpError(hr, error, "InitializeSecurityDescriptor");
    }

    CSASSERT(m_pUserSid && IsValidSid(m_pUserSid));

    dwDaclSize =  sizeof(ACL) +
        sizeof(ACCESS_ALLOWED_ACE)-sizeof(DWORD)+
        GetLengthSid(m_pUserSid);
    
    m_pDaclPrivileges = (PACL)LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, dwDaclSize);
    _JumpIfAllocFailed(m_pDaclPrivileges, error);

    if(!InitializeAcl(m_pDaclPrivileges, dwDaclSize, ACL_REVISION))
    {
        hr = myHLastError();
        _JumpError(hr, error, "InitializeAcl");
    }

    if(!AddAccessAllowedAce(
            m_pDaclPrivileges,
            ACL_REVISION,
            dwRoles,
            m_pUserSid))
    {
        hr = myHLastError();
        _JumpError(hr, error, "AddAccessAllowedAce");
    }

    if(!GetSecurityDescriptorOwner(m_pCASD,
                                  &pOwnerSid,
                                  &fDefaulted))
    {
        hr = myHLastError();
        _JumpError(hr, error, "GetSecurityDescriptorOwner");
    }

    if(!SetSecurityDescriptorOwner(m_pSDPrivileges,
                                  pOwnerSid,
                                  fDefaulted))
    {
        hr = myHLastError();
        _JumpError(hr, error, "SetSecurityDescriptorOwner");
    }

    if(!GetSecurityDescriptorGroup(m_pCASD,
                                  &pGroupSid,
                                  &fDefaulted))
    {
        hr = myHLastError();
        _JumpError(hr, error, "GetSecurityDescriptorGroup");
    }

    if(!SetSecurityDescriptorGroup(m_pSDPrivileges,
                                  pGroupSid,
                                  fDefaulted))
    {
        hr = myHLastError();
        _JumpError(hr, error, "SetSecurityDescriptorGroup");
    }

    if(!SetSecurityDescriptorDacl(m_pSDPrivileges,
                                  TRUE,
                                  m_pDaclPrivileges,
                                  FALSE))
    {
        hr = myHLastError();
        _JumpError(hr, error, "SetSecurityDescriptorDacl");
    }

    CSASSERT(IsValidSecurityDescriptor(m_pSDPrivileges));

error:

    if(S_OK != hr)
    {
        if(m_pDaclPrivileges)
        {
            LocalFree(m_pDaclPrivileges);
            m_pDaclPrivileges = NULL;
        }

        if(m_pSDPrivileges)
        {
            LocalFree(m_pSDPrivileges);
            m_pSDPrivileges = NULL;
        }
    }
    return hr;
}

HRESULT CAuditEvent::BuildAuditParamArray(PAUDIT_PARAM& rpParamArray)
{
    HRESULT hr = S_OK;

    // number of parameters added should be the same as the number of
    // params defined in the audit format string in msaudite.dll
    CSASSERT(m_cEventData == m_cRequiredEventData);

    rpParamArray = (PAUDIT_PARAM) LocalAlloc(
        LMEM_FIXED | LMEM_ZEROINIT, 
        sizeof(AUDIT_PARAM)*m_cEventData);
    _JumpIfAllocFailed(rpParamArray, error);

    for(USHORT c=0;c<m_cEventData;c++)
    {
        rpParamArray[c].Type = APT_String;
        
        hr = m_pEventDataList[c]->ConvertToString(
            &rpParamArray[c].String);
        _JumpIfError(hr, error, "ConvertToString");
    }

error:
    return hr;
}

void CAuditEvent::FreeAuditParamArray(PAUDIT_PARAM pParamArray)
{
    if(pParamArray)
    {
        for(USHORT c=0;c<m_cEventData;c++)
        {
            if(pParamArray[c].String)
                delete[]  pParamArray[c].String;
        }

        LocalFree(pParamArray);
    }
}