// ************************************************************************** // Copyright (c) Microsoft Corporation, All Rights Reserved // // File: EVPROV.cpp // // Description: // Sample event provider. // // History: // // ************************************************************************** #ifndef _WIN32_DCOM #define _WIN32_DCOM #endif #include #include #include #include #include "oahelp.inl" #include "evprov.h" #include #include #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 ; }