519 lines
12 KiB
C++
519 lines
12 KiB
C++
// **************************************************************************
|
|
|
|
// Copyright (c) Microsoft Corporation, All Rights Reserved
|
|
//
|
|
// File: EVPROV.cpp
|
|
//
|
|
// Description:
|
|
// Sample event provider.
|
|
//
|
|
// History:
|
|
//
|
|
// **************************************************************************
|
|
|
|
#ifndef _WIN32_DCOM
|
|
#define _WIN32_DCOM
|
|
#endif
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <objbase.h>
|
|
|
|
#include <wbemidl.h>
|
|
|
|
#include "oahelp.inl"
|
|
#include "evprov.h"
|
|
|
|
#include <strsafe.h>
|
|
#include <sddl.h>
|
|
|
|
|
|
#define MASK_CLIENT_ACCESS_BIND 1
|
|
static GENERIC_MAPPING s_ClientAccessMapping = {
|
|
0,
|
|
0,
|
|
STANDARD_RIGHTS_REQUIRED | MASK_CLIENT_ACCESS_BIND,
|
|
STANDARD_RIGHTS_REQUIRED | MASK_CLIENT_ACCESS_BIND
|
|
};
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CMyEventProvider::CMyEventProvider()
|
|
{
|
|
m_pNs = 0;
|
|
m_pSink = 0;
|
|
m_cRef = 0;
|
|
m_pEventClassDef = 0;
|
|
m_eStatus = Pending;
|
|
m_hThread = 0;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
CMyEventProvider::~CMyEventProvider()
|
|
{
|
|
if (m_hThread)
|
|
CloseHandle(m_hThread);
|
|
|
|
if (m_pNs)
|
|
m_pNs->Release();
|
|
|
|
if (m_pSink)
|
|
m_pSink->Release();
|
|
|
|
if (m_pEventClassDef)
|
|
m_pEventClassDef->Release();
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
STDMETHODIMP CMyEventProvider::QueryInterface(REFIID riid, LPVOID * ppv)
|
|
{
|
|
*ppv = 0;
|
|
|
|
if (IID_IUnknown==riid || IID_IWbemEventProvider==riid)
|
|
{
|
|
*ppv = (IWbemEventProvider *) this;
|
|
AddRef();
|
|
return NOERROR;
|
|
}
|
|
|
|
if (IID_IWbemProviderInit==riid)
|
|
{
|
|
*ppv = (IWbemProviderInit *) this;
|
|
AddRef();
|
|
return NOERROR;
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
ULONG CMyEventProvider::AddRef()
|
|
{
|
|
return ++m_cRef;
|
|
}
|
|
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
ULONG CMyEventProvider::Release()
|
|
{
|
|
if (0 != --m_cRef)
|
|
return m_cRef;
|
|
|
|
// If here, we are shutting down.
|
|
// ==============================
|
|
|
|
m_eStatus = PendingStop;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
HRESULT CMyEventProvider::ProvideEvents(
|
|
/* [in] */ IWbemObjectSink __RPC_FAR *pSink,
|
|
/* [in] */ long lFlags
|
|
)
|
|
{
|
|
// Copy the sink.
|
|
// ==============
|
|
|
|
m_pSink = pSink;
|
|
m_pSink->AddRef();
|
|
|
|
// Create the event thread.
|
|
// ========================
|
|
|
|
DWORD dwTID;
|
|
|
|
m_hThread = CreateThread(
|
|
0,
|
|
0,
|
|
CMyEventProvider::EventThread,
|
|
this,
|
|
0,
|
|
&dwTID
|
|
);
|
|
|
|
|
|
// Wait for provider to be 'ready'.
|
|
// ================================
|
|
|
|
while (m_eStatus != Running)
|
|
Sleep(100);
|
|
|
|
return WBEM_NO_ERROR;
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// This particular provider, being in a DLL operates via its own thread.
|
|
//
|
|
// In practice, such a provider would probably be implemented within a
|
|
// separate EXE.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
DWORD WINAPI CMyEventProvider::EventThread(LPVOID pArg)
|
|
{
|
|
// Make transition to the per-instance method.
|
|
// ===========================================
|
|
|
|
((CMyEventProvider *)pArg)->InstanceThread();
|
|
return 0;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// Events are generated from here
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
void CMyEventProvider::InstanceThread()
|
|
{
|
|
int nIteration = 0;
|
|
|
|
CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
|
|
m_eStatus = Running;
|
|
|
|
while (m_eStatus == Running)
|
|
{
|
|
Sleep(2000); // Provide an event every two seconds
|
|
|
|
|
|
// Generate a new event object.
|
|
// ============================
|
|
|
|
IWbemClassObject *pEvt = 0;
|
|
|
|
HRESULT hRes = m_pEventClassDef->SpawnInstance(0, &pEvt);
|
|
if (hRes != 0)
|
|
continue; // Failed
|
|
|
|
|
|
// Generate some values to put in the event.
|
|
// =========================================
|
|
|
|
wchar_t Buf[128];
|
|
StringCbPrintfW(Buf, sizeof(Buf), L"Test Event <%d>", nIteration);
|
|
|
|
CVARIANT vName(Buf);
|
|
hRes = pEvt->Put(CBSTR(L"Name"), 0, vName, 0);
|
|
if (FAILED(hRes))
|
|
{
|
|
// If here, delivery failed. This could be due to an out of memory condition.
|
|
// Do something to report it.
|
|
}
|
|
|
|
CVARIANT vCount((LONG) nIteration);
|
|
hRes = pEvt->Put(CBSTR(L"Value"), 0, vCount, 0);
|
|
if (FAILED(hRes))
|
|
{
|
|
// If here, delivery failed. This could be due to an out of memory condition.
|
|
// Do something to report it.
|
|
}
|
|
|
|
// Deliver the event to CIMOM.
|
|
// ============================
|
|
|
|
hRes = m_pSink->Indicate(1, &pEvt);
|
|
|
|
if (FAILED(hRes))
|
|
{
|
|
// If here, delivery failed. Do something to report it.
|
|
}
|
|
|
|
pEvt->Release();
|
|
nIteration++;
|
|
}
|
|
|
|
// When we get to here, we are no longer interested in the
|
|
// provider and Release() has long since returned.
|
|
|
|
m_eStatus = Stopped;
|
|
delete this;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
//***************************************************************************
|
|
|
|
// Inherited from IWbemProviderInit
|
|
// ================================
|
|
|
|
HRESULT CMyEventProvider::Initialize(
|
|
/* [in] */ LPWSTR pszUser,
|
|
/* [in] */ LONG lFlags,
|
|
/* [in] */ LPWSTR pszNamespace,
|
|
/* [in] */ LPWSTR pszLocale,
|
|
/* [in] */ IWbemServices __RPC_FAR *pNamespace,
|
|
/* [in] */ IWbemContext __RPC_FAR *pCtx,
|
|
/* [in] */ IWbemProviderInitSink __RPC_FAR *pInitSink
|
|
)
|
|
{
|
|
// We don't care about most of the incoming parameters in this
|
|
// simple sample. However, we will save the namespace pointer
|
|
// and get our event class definition.
|
|
// ===========================================================
|
|
if(! pNamespace)
|
|
{
|
|
pInitSink->SetStatus(WBEM_E_FAILED , 0);
|
|
return WBEM_NO_ERROR;
|
|
}
|
|
|
|
m_pNs = pNamespace;
|
|
m_pNs->AddRef();
|
|
|
|
|
|
// Grab the class definition for the event.
|
|
// ======================================
|
|
|
|
IWbemClassObject *pObj = 0;
|
|
|
|
HRESULT hRes = m_pNs->GetObject(
|
|
CBSTR(EVENTCLASS),
|
|
0,
|
|
pCtx,
|
|
&pObj,
|
|
0
|
|
);
|
|
|
|
if (hRes != 0)
|
|
{
|
|
pInitSink->SetStatus(WBEM_E_FAILED , 0);
|
|
return WBEM_NO_ERROR;
|
|
}
|
|
|
|
m_pEventClassDef = pObj;
|
|
|
|
// Tell CIMOM that we're up and running.
|
|
// =====================================
|
|
|
|
pInitSink->SetStatus(WBEM_S_INITIALIZED,0);
|
|
|
|
return WBEM_NO_ERROR;
|
|
}
|
|
|
|
// ================================
|
|
// Inherited from IWbemEventProviderSecurity. This sample does not analyze the query and
|
|
// applies the same policy to all queries.
|
|
// ================================
|
|
|
|
HRESULT CMyEventProvider::AccessCheck(
|
|
WBEM_CWSTR wszQueryLanguage,
|
|
WBEM_CWSTR wszQuery,
|
|
long lSidLength,
|
|
const BYTE* pSid)
|
|
{
|
|
|
|
HRESULT hr = WBEM_S_NO_ERROR;
|
|
|
|
//this is a temporary subscription, and therefore we have the thread token
|
|
|
|
//Grant access for built-in admins, local system, local admins, local service and network service.
|
|
PSECURITY_DESCRIPTOR secDescriptor = NULL;
|
|
BOOL bRes = ConvertStringSecurityDescriptorToSecurityDescriptor //this function is only available on Windows 2000 and above
|
|
( "O:BAG:BAD:(A;;0x10000001;;;BA)(A;;0x10000001;;;SY)(A;;0x10000001;;;LA)(A;;0x10000001;;;SY)(A;;0x10000001;;;S-1-5-20)(A;;0x10000001;;;S-1-5-19)",
|
|
SDDL_REVISION_1,
|
|
(PSECURITY_DESCRIPTOR *) &secDescriptor,
|
|
NULL);
|
|
|
|
if (! bRes)
|
|
{
|
|
return WBEM_E_ACCESS_DENIED;
|
|
}
|
|
|
|
if (pSid == NULL)
|
|
{
|
|
|
|
|
|
hr = CoImpersonateClient () ;
|
|
if ( FAILED ( hr ) )
|
|
{
|
|
LocalFree(secDescriptor);
|
|
return WBEM_E_ACCESS_DENIED ;
|
|
}
|
|
|
|
// perform an access check.
|
|
|
|
hr = CheckAccess((SECURITY_DESCRIPTOR *)secDescriptor,
|
|
MASK_CLIENT_ACCESS_BIND,
|
|
&s_ClientAccessMapping);
|
|
|
|
LocalFree(secDescriptor);
|
|
|
|
// Revert before we perform any operations
|
|
CoRevertToSelf () ;
|
|
|
|
if (FAILED(hr))
|
|
return WBEM_E_ACCESS_DENIED;
|
|
else
|
|
return WBEM_S_NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
//check against the SID passed to us. This op
|
|
|
|
// Call GetSecurityDescriptorDacl() to obtain the DACL for secDescriptor.
|
|
// Then, retrieve the access mask corresponding to permissions granted
|
|
// by DACL to account denoted in pSid. Please refer to Security SDK samples
|
|
// and documentation for more details.
|
|
|
|
LocalFree(secDescriptor);
|
|
|
|
if(0) //pSID is not privileged enough to receive events from this proviuder
|
|
return WBEM_E_ACCESS_DENIED;
|
|
else
|
|
return WBEM_S_NO_ERROR;
|
|
|
|
}
|
|
|
|
return WBEM_S_NO_ERROR;
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* Name: CheckAccess
|
|
*
|
|
* Description: Allow provider to evaluate permissions against a security descriptor
|
|
*
|
|
* This method should be called by WMI providers in scenarios where
|
|
* they cannot or should not impersonate the client. This happens in two scenarios:
|
|
* a) when the providers access resources that are not protected by ACL's
|
|
* b) when the client connects at the impersonation level of RPC_C_IMP_LEVEL_IDENTIFY
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HRESULT CMyEventProvider::CheckAccess (SECURITY_DESCRIPTOR *a_SecurityDescriptor ,
|
|
DWORD a_Access ,
|
|
GENERIC_MAPPING *a_Mapping)
|
|
{
|
|
HRESULT t_Result = S_OK ;
|
|
|
|
HANDLE t_Token = NULL ;
|
|
|
|
BOOL t_Status = OpenThreadToken (
|
|
|
|
GetCurrentThread () ,
|
|
TOKEN_QUERY ,
|
|
TRUE ,
|
|
& t_Token
|
|
) ;
|
|
|
|
DWORD t_LastError = GetLastError () ;
|
|
if ( ! t_Status)
|
|
{
|
|
//the thread token should always be available
|
|
|
|
if ( t_LastError ==E_ACCESSDENIED)
|
|
return WBEM_E_ACCESS_DENIED ;
|
|
else
|
|
return WBEM_E_FAILED ;
|
|
|
|
}
|
|
|
|
|
|
DWORD t_Access = 0 ;
|
|
BOOL t_AccessStatus = FALSE ;
|
|
PRIVILEGE_SET *t_PrivilegeSet = NULL ;
|
|
DWORD t_PrivilegeSetSize = 0 ;
|
|
|
|
MapGenericMask (
|
|
|
|
& a_Access ,
|
|
a_Mapping
|
|
) ;
|
|
|
|
t_Status = ::AccessCheck (
|
|
|
|
a_SecurityDescriptor ,
|
|
t_Token,
|
|
a_Access ,
|
|
a_Mapping ,
|
|
NULL ,
|
|
& t_PrivilegeSetSize ,
|
|
& t_Access ,
|
|
& t_AccessStatus
|
|
) ;
|
|
|
|
if (!t_Status || !t_AccessStatus )
|
|
{
|
|
DWORD t_LastError = GetLastError () ;
|
|
if ( t_LastError == ERROR_INSUFFICIENT_BUFFER )
|
|
{
|
|
t_PrivilegeSet = ( PRIVILEGE_SET * ) new BYTE [ t_PrivilegeSetSize ] ;
|
|
if ( t_PrivilegeSet )
|
|
{
|
|
t_Status = ::AccessCheck (
|
|
a_SecurityDescriptor ,
|
|
t_Token,
|
|
a_Access ,
|
|
a_Mapping ,
|
|
t_PrivilegeSet ,
|
|
& t_PrivilegeSetSize ,
|
|
& t_Access ,
|
|
& t_AccessStatus
|
|
) ;
|
|
|
|
if ( !t_Status || !t_AccessStatus )
|
|
{
|
|
t_Result = WBEM_E_ACCESS_DENIED ;
|
|
}
|
|
|
|
delete [] ( BYTE * ) t_PrivilegeSet ;
|
|
}
|
|
else
|
|
{
|
|
t_Result = WBEM_E_OUT_OF_MEMORY ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
t_Result = WBEM_E_ACCESS_DENIED;
|
|
}
|
|
|
|
}
|
|
|
|
CloseHandle ( t_Token ) ;
|
|
|
|
|
|
return t_Result ;
|
|
}
|
|
|