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

873 lines
24 KiB
C++

/*++
Copyright (C) 1996-2001 Microsoft Corporation
Module Name:
REGEPROV.CPP
Abstract:
History:
--*/
#include "..\precomp.h"
#include <wbemidl.h>
#include <stdio.h>
#include "cfdyn.h"
#include "stdprov.h"
#include "regeprov.h"
#include <sync.h>
#include <tss.h>
#include <genutils.h>
#include <analyser.h>
#include <cominit.h>
#include <GroupsForUser.h>
CRegEventProvider::CRegEventProvider()
: m_lRef(0), m_hThread(NULL), m_dwId(NULL), m_pGenerator(NULL),
m_pKeyClass(NULL), m_pValueClass(NULL), m_pTreeClass(NULL), m_pSink(NULL)
{
InitializeCriticalSection(&m_cs);
m_pbEnd = new BOOL(FALSE);
m_hResyncEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
}
CRegEventProvider::~CRegEventProvider()
{
if(m_pSink)
m_pSink->Release();
if(m_pKeyClass)
m_pKeyClass->Release();
if(m_pValueClass)
m_pValueClass->Release();
if(m_pTreeClass)
m_pTreeClass->Release();
delete m_pGenerator;
DeleteCriticalSection(&m_cs);
InterlockedDecrement(&lObj);
CloseHandle(m_hResyncEvent);
CloseHandle(m_hThread);
delete m_pbEnd;
}
STDMETHODIMP CRegEventProvider::QueryInterface(REFIID riid, void** ppv)
{
if(riid == IID_IWbemEventProvider)
{
*ppv = (IWbemEventProvider*)this;
AddRef();
return S_OK;
}
else if(riid == IID_IWbemEventProviderQuerySink)
{
*ppv = (IWbemEventProviderQuerySink*)this;
AddRef();
return S_OK;
}
else if(riid == IID_IWbemEventProviderSecurity)
{
*ppv = (IWbemEventProviderSecurity*)this;
AddRef();
return S_OK;
}
else if(riid == IID_IWbemProviderInit)
{
*ppv = (IWbemProviderInit*)this;
AddRef();
return S_OK;
}
else return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE CRegEventProvider::AddRef()
{
return InterlockedIncrement(&m_lRef);
}
ULONG STDMETHODCALLTYPE CRegEventProvider::Release()
{
long lRef = InterlockedDecrement(&m_lRef);
if(lRef == 0)
{
*m_pbEnd = TRUE;
m_lRef = 1;
HANDLE hThread = m_hThread;
CycleWorker();
WaitForSingleObject(hThread, 5000);
}
return lRef;
}
STDMETHODIMP CRegEventProvider::Initialize(LPWSTR wszUser, long lFlags,
LPWSTR wszNamespace,
LPWSTR wszLocale, IWbemServices* pNamespace, IWbemContext* pCtx,
IWbemProviderInitSink* pSink)
{
HRESULT hres = pNamespace->GetObject(REG_KEY_EVENT_CLASS, 0, NULL,
&m_pKeyClass, NULL);
hres = pNamespace->GetObject(REG_VALUE_EVENT_CLASS, 0, NULL,
&m_pValueClass, NULL);
hres = pNamespace->GetObject(REG_TREE_EVENT_CLASS, 0, NULL,
&m_pTreeClass, NULL);
if(IsNT())
{
m_hThread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)&CRegEventProvider::Worker,
this, 0, &m_dwId);
}
else
{
m_pGenerator = new CTimerGenerator;
}
pSink->SetStatus(hres, 0);
return hres;
}
STDMETHODIMP CRegEventProvider::ProvideEvents(
IWbemObjectSink* pSink, long lFlags)
{
m_pSink = pSink;
pSink->AddRef();
return S_OK;
}
HRESULT CRegEventProvider::AddRequest(CRegistryEventRequest* pNewReq)
{
// Search for a similar request
// ============================
for(int i = 0; i < m_apRequests.GetSize(); i++)
{
CRegistryEventRequest* pReq = m_apRequests[i];
if(pReq->IsActive() && pReq->IsSameAs(pNewReq))
{
// Found it!
// =========
HRESULT hres = pReq->Reactivate(pNewReq->GetPrimaryId(),
pNewReq->GetMsWait());
delete pNewReq;
return hres;
}
}
// Not found. Add it
// =================
HRESULT hres = pNewReq->Activate();
if(SUCCEEDED(hres))
{
m_apRequests.Add(pNewReq);
}
return hres;
}
STDMETHODIMP CRegEventProvider::NewQuery(DWORD dwId, WBEM_WSTR wszLanguage,
WBEM_WSTR wszQuery)
{
HRESULT hres;
// Remove whatever requests we may have had for this id
// ====================================================
{
CInCritSec ics(&m_cs);
// Remove all requests with this Is
// ================================
for(int i = 0; i < m_apRequests.GetSize(); i++)
{
CRegistryEventRequest* pReq = m_apRequests[i];
if(pReq->DoesContainId(dwId))
{
pReq->Deactivate(dwId);
}
}
}
// Parse the query
// ===============
CTextLexSource Source(wszQuery);
QL1_Parser Parser(&Source);
QL_LEVEL_1_RPN_EXPRESSION* pExpr;
if(Parser.Parse(&pExpr))
{
return WBEM_E_INVALID_QUERY;
}
CDeleteMe<QL_LEVEL_1_RPN_EXPRESSION> dm(pExpr);
// Check the class
// ===============
int nEventType;
if(!_wcsicmp(pExpr->bsClassName, REG_VALUE_EVENT_CLASS))
{
nEventType = e_RegValueChange;
}
else if(!_wcsicmp(pExpr->bsClassName, REG_KEY_EVENT_CLASS))
{
nEventType = e_RegKeyChange;
}
else if(!_wcsicmp(pExpr->bsClassName, REG_TREE_EVENT_CLASS))
{
nEventType = e_RegTreeChange;
}
else
{
// No such class
// =============
return WBEM_E_INVALID_QUERY;
}
// Check tolerance on Win95
// ========================
if(!IsNT() && pExpr->Tolerance.m_bExact)
{
return WBEMESS_E_REGISTRATION_TOO_PRECISE;
}
// Extract the values of hive from the query
// =========================================
CPropertyName Name;
Name.AddElement(REG_HIVE_PROPERTY_NAME);
CWStringArray awsHiveVals;
hres = GetValuesForProp(pExpr, Name, awsHiveVals);
if(FAILED(hres)) return hres;
// Translate them to real hives
// ============================
CUniquePointerArray<HKEY> aHives;
for(int i = 0; i < awsHiveVals.Size(); i++)
{
HKEY hHive = TranslateHiveName(awsHiveVals[i]);
if(hHive == NULL)
{
return WBEM_E_INVALID_QUERY;
}
aHives.Add(new HKEY(hHive));
}
// Extract the values of key from the query
// ========================================
Name.Empty();
if(nEventType == e_RegTreeChange)
{
Name.AddElement(REG_ROOT_PROPERTY_NAME);
}
else
{
Name.AddElement(REG_KEY_PROPERTY_NAME);
}
CWStringArray awsKeyVals;
hres = GetValuesForProp(pExpr, Name, awsKeyVals);
if(FAILED(hres))
{
return hres;
}
CWStringArray awsValueVals;
if(nEventType == e_RegValueChange)
{
// Extract the values for the value
// ================================
Name.Empty();
Name.AddElement(REG_VALUE_PROPERTY_NAME);
hres = GetValuesForProp(pExpr, Name, awsValueVals);
if(FAILED(hres))
{
return hres;
}
}
HRESULT hresGlobal = WBEM_E_INVALID_QUERY;
{
CInCritSec ics(&m_cs); // do this in a critical section
// Go through every combination of the above and create requests
// =============================================================
for(int nHiveIndex = 0; nHiveIndex < aHives.GetSize(); nHiveIndex++)
{
HKEY hHive = *aHives[nHiveIndex];
LPWSTR wszHive = awsHiveVals[nHiveIndex];
for(int nKeyIndex = 0; nKeyIndex < awsKeyVals.Size(); nKeyIndex++)
{
LPWSTR wszKey = awsKeyVals[nKeyIndex];
if(nEventType == e_RegValueChange)
{
for(int nValueIndex = 0; nValueIndex < awsValueVals.Size();
nValueIndex++)
{
LPWSTR wszValue = awsValueVals[nValueIndex];
CRegistryEventRequest* pReq =
new CRegistryValueEventRequest(this,
pExpr->Tolerance,
dwId, hHive, wszHive, wszKey, wszValue);
if(pReq->IsOK())
{
HRESULT hres = AddRequest(pReq);
if(SUCCEEDED(hres))
hresGlobal = hres;
}
else
{
DEBUGTRACE((LOG_ESS, "Invalid registration: key "
"%S, value %S\n", wszKey, wszValue));
delete pReq;
}
}
}
else
{
// Value-less request
// ==================
CRegistryEventRequest* pReq;
if(nEventType == e_RegKeyChange)
{
pReq = new CRegistryKeyEventRequest(this,
pExpr->Tolerance,
dwId, hHive, wszHive, wszKey);
}
else
{
pReq = new CRegistryTreeEventRequest(this,
pExpr->Tolerance,
dwId, hHive, wszHive, wszKey);
}
if(pReq->IsOK())
{
hres = AddRequest(pReq);
if(SUCCEEDED(hres))
hresGlobal = hres;
}
else
{
DEBUGTRACE((LOG_ESS, "Invalid registration: key %S\n",
wszKey));
delete pReq;
}
}
}
}
} // out of critical section
// Inform the polling thread that it has new work
// ==============================================
CycleWorker();
return hresGlobal;
}
STDMETHODIMP CRegEventProvider::AccessCheck(WBEM_CWSTR wszLanguage,
WBEM_CWSTR wszQuery,
long lSidLength,
const BYTE* aSid)
{
HRESULT hres;
if(!IsNT())
return WBEM_S_FALSE;
PSID pSid = (PSID)aSid;
HANDLE hToken = NULL;
if(pSid == NULL)
{
//
// Access check based on the thread
//
hres = WbemCoImpersonateClient();
if(FAILED(hres))
return hres;
BOOL bRes = OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE,
&hToken);
WbemCoRevertToSelf();
if(!bRes)
{
return WBEM_E_ACCESS_DENIED;
}
}
CCloseMe cm1(hToken);
// Parse the query
// ===============
CTextLexSource Source(wszQuery);
QL1_Parser Parser(&Source);
QL_LEVEL_1_RPN_EXPRESSION* pExpr;
if(Parser.Parse(&pExpr))
{
return WBEM_E_INVALID_QUERY;
}
CDeleteMe<QL_LEVEL_1_RPN_EXPRESSION> dm(pExpr);
// Check the class
// ===============
int nEventType;
if(!_wcsicmp(pExpr->bsClassName, REG_VALUE_EVENT_CLASS))
{
nEventType = e_RegValueChange;
}
else if(!_wcsicmp(pExpr->bsClassName, REG_KEY_EVENT_CLASS))
{
nEventType = e_RegKeyChange;
}
else if(!_wcsicmp(pExpr->bsClassName, REG_TREE_EVENT_CLASS))
{
nEventType = e_RegTreeChange;
}
else
{
// No such class
// =============
return WBEM_E_INVALID_QUERY;
}
// Extract the values of hive from the query
// =========================================
CPropertyName Name;
Name.AddElement(REG_HIVE_PROPERTY_NAME);
CWStringArray awsHiveVals;
hres = GetValuesForProp(pExpr, Name, awsHiveVals);
if(FAILED(hres)) return hres;
// Translate them to real hives
// ============================
CUniquePointerArray<HKEY> aHives;
for(int i = 0; i < awsHiveVals.Size(); i++)
{
HKEY hHive = TranslateHiveName(awsHiveVals[i]);
if(hHive == NULL)
{
return WBEM_E_INVALID_QUERY;
}
aHives.Add(new HKEY(hHive));
}
// Extract the values of key from the query
// ========================================
Name.Empty();
if(nEventType == e_RegTreeChange)
{
Name.AddElement(REG_ROOT_PROPERTY_NAME);
}
else
{
Name.AddElement(REG_KEY_PROPERTY_NAME);
}
CWStringArray awsKeyVals;
hres = GetValuesForProp(pExpr, Name, awsKeyVals);
if(FAILED(hres))
{
return hres;
}
HRESULT hresGlobal = WBEM_E_INVALID_QUERY;
// Go through every combination of the above and create requests
// =============================================================
for(int nHiveIndex = 0; nHiveIndex < aHives.GetSize(); nHiveIndex++)
{
HKEY hHive = *aHives[nHiveIndex];
LPWSTR wszHive = awsHiveVals[nHiveIndex];
for(int nKeyIndex = 0; nKeyIndex < awsKeyVals.Size(); nKeyIndex++)
{
LPWSTR wszKey = awsKeyVals[nKeyIndex];
// Get that key's security
// =======================
HKEY hKey;
long lRes = RegOpenKeyExW(hHive, wszKey, 0, READ_CONTROL, &hKey);
if(lRes)
return WBEM_E_NOT_FOUND;
CRegCloseMe cm2(hKey);
DWORD dwLen = 0;
lRes = RegGetKeySecurity(hKey,
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION,
NULL,
&dwLen);
if(lRes != ERROR_INSUFFICIENT_BUFFER)
return WBEM_E_FAILED;
PSECURITY_DESCRIPTOR pDesc = (PSECURITY_DESCRIPTOR)new BYTE[dwLen];
if(pDesc == NULL)
return WBEM_E_OUT_OF_MEMORY;
CVectorDeleteMe<BYTE> vdm((BYTE*)pDesc);
lRes = RegGetKeySecurity(hKey,
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION,
pDesc,
&dwLen);
if(lRes)
return WBEM_E_FAILED;
//
// Check permissions differently depending on whether we have a SID
// or an actual token
//
if(pSid)
{
//
// We have a SID --- walk the ACL
//
//
// Extract the ACL
//
PACL pAcl = NULL;
BOOL bAclPresent, bAclDefaulted;
if(!GetSecurityDescriptorDacl(pDesc, &bAclPresent, &pAcl,
&bAclDefaulted))
{
return WBEM_E_FAILED;
}
if(bAclPresent)
{
//
// This is our own ACL walker
//
DWORD dwAccessMask;
NTSTATUS st = GetAccessMask((PSID)pSid, pAcl,
&dwAccessMask);
if(st)
{
ERRORTRACE((LOG_ESS, "Registry event provider unable "
"to retrieve access mask for the creator of "
"registration %S: NT status %d.\n"
"Registration disabled\n", wszQuery));
return WBEM_E_FAILED;
}
if((dwAccessMask & KEY_NOTIFY) == 0)
return WBEM_E_ACCESS_DENIED;
}
}
else
{
//
// We have a token --- use AccessCheck
//
//
// Construct generic mapping for registry keys
//
GENERIC_MAPPING map;
map.GenericRead = KEY_READ;
map.GenericWrite = KEY_WRITE;
map.GenericExecute = KEY_EXECUTE;
map.GenericAll = KEY_ALL_ACCESS;
//
// Construct privilege array receptacle
//
PRIVILEGE_SET ps[10];
DWORD dwSize = 10 * sizeof(PRIVILEGE_SET);
DWORD dwGranted;
BOOL bResult;
BOOL bOK = ::AccessCheck(pDesc, hToken, KEY_NOTIFY, &map, ps,
&dwSize, &dwGranted, &bResult);
if(!bOK || !bResult)
return WBEM_E_ACCESS_DENIED;
}
}
}
return WBEM_S_NO_ERROR;
}
STDMETHODIMP CRegEventProvider::CancelQuery(DWORD dwId)
{
CInCritSec ics(&m_cs);
// Remove all requests with this Is
// ================================
for(int i = 0; i < m_apRequests.GetSize(); i++)
{
CRegistryEventRequest* pReq = m_apRequests[i];
if(pReq->DoesContainId(dwId))
{
pReq->Deactivate(dwId);
}
}
// Inform the polling thread that it has new work
// ==============================================
CycleWorker();
return WBEM_S_NO_ERROR;
}
HRESULT CRegEventProvider::CycleWorker()
{
SetEvent(m_hResyncEvent);
return S_OK;
}
HRESULT CRegEventProvider::GetValuesForProp(QL_LEVEL_1_RPN_EXPRESSION* pExpr,
CPropertyName& PropName, CWStringArray& awsVals)
{
awsVals.Empty();
// Get the necessary query
// =======================
QL_LEVEL_1_RPN_EXPRESSION* pPropExpr;
HRESULT hres = CQueryAnalyser::GetNecessaryQueryForProperty(pExpr,
PropName, pPropExpr);
if(FAILED(hres))
{
return hres;
}
// See if there are any tokens
// ===========================
if(pPropExpr->nNumTokens == 0)
{
delete pPropExpr;
return WBEMESS_E_REGISTRATION_TOO_BROAD;
}
// Combine them all
// ================
for(int i = 0; i < pPropExpr->nNumTokens; i++)
{
QL_LEVEL_1_TOKEN& Token = pPropExpr->pArrayOfTokens[i];
if(Token.nTokenType == QL1_NOT)
{
delete pPropExpr;
return WBEMESS_E_REGISTRATION_TOO_BROAD;
}
else if(Token.nTokenType == QL1_AND || Token.nTokenType == QL1_OR)
{
// We treat them all as ORs
// ========================
}
else
{
// This is a token
// ===============
if(Token.nOperator != QL1_OPERATOR_EQUALS)
{
delete pPropExpr;
return WBEMESS_E_REGISTRATION_TOO_BROAD;
}
if(V_VT(&Token.vConstValue) != VT_BSTR)
{
delete pPropExpr;
return WBEM_E_INVALID_QUERY;
}
// This token is a string equality. Add the string to the list
// ===========================================================
awsVals.Add(V_BSTR(&Token.vConstValue));
}
}
delete pPropExpr;
return WBEM_S_NO_ERROR;
}
HRESULT CRegEventProvider::SetTimerInstruction(CTimerInstruction* pInst)
{
return m_pGenerator->Set(pInst, CWbemTime::GetZero());
}
HRESULT CRegEventProvider::RemoveTimerInstructions(
CRegistryInstructionTest* pTest)
{
return m_pGenerator->Remove(pTest);
}
HRESULT CRegEventProvider::RaiseEvent(IWbemClassObject* pEvent)
{
if(m_pSink)
return m_pSink->Indicate(1, &pEvent);
else
return WBEM_S_NO_ERROR;
}
HKEY CRegEventProvider::TranslateHiveName(LPCWSTR wszName)
{
if(!_wcsicmp(wszName, L"HKEY_CLASSES_ROOT"))
return HKEY_CLASSES_ROOT;
/* Disallowed: different semantics for client and server
else if(!_wcsicmp(wszName, L"HKEY_CURRENT_USER"))
return HKEY_CURRENT_USER;
*/
else if(!_wcsicmp(wszName, L"HKEY_LOCAL_MACHINE"))
return HKEY_LOCAL_MACHINE;
else if(!_wcsicmp(wszName, L"HKEY_USERS"))
return HKEY_USERS;
else if(!_wcsicmp(wszName, L"HKEY_PERFORMANCE_DATA"))
return HKEY_PERFORMANCE_DATA;
else if(!_wcsicmp(wszName, L"HKEY_CURRENT_CONFIG"))
return HKEY_CURRENT_CONFIG;
else if(!_wcsicmp(wszName, L"HKEY_DYN_DATA"))
return HKEY_DYN_DATA;
else
return NULL;
}
DWORD CRegEventProvider::Worker(void* p)
{
CRegEventProvider* pThis = (CRegEventProvider*)p;
BOOL* pbEnd = pThis->m_pbEnd;
while(!*pbEnd)
{
// Construct an array of handles to wait on
// ========================================
HANDLE* aHandles = NULL;
int nNumHandles = 0;
{
CInCritSec ics(&pThis->m_cs);
aHandles = new HANDLE[pThis->m_apRequests.GetSize()+1];
nNumHandles = 0;
for(int i = 0; i < pThis->m_apRequests.GetSize(); i++)
{
CRegistryEventRequest* pReq = pThis->m_apRequests[i];
if(!pReq->IsActive())
{
pThis->m_apRequests.RemoveAt(i);
i--;
}
else
{
if(pReq->IsNew())
pReq->ResetOnChangeHandle();
aHandles[nNumHandles++] = pReq->GetOnChangeHandle();
}
}
// Add the standard event
// ======================
aHandles[nNumHandles++] = pThis->m_hResyncEvent;
}
// Wait for something
// ==================
DWORD dwRes = WaitForMultipleObjects(nNumHandles, aHandles, FALSE,
INFINITE);
delete [] aHandles;
{
CInCritSec ics(&pThis->m_cs);
// Check if a resync occurred
// ==========================
if(dwRes == WAIT_FAILED)
{
DEBUGTRACE((LOG_ESS, "CRITICAL ERROR: wait failure!!! Error code %d\n",
GetLastError()));
Sleep(1000);
continue;
}
if(dwRes == WAIT_OBJECT_0 + nNumHandles - 1 || dwRes == WAIT_FAILED)
{
continue;
}
// Check which handle it is
// ========================
int nIndex = dwRes - WAIT_OBJECT_0;
if(nIndex >= 0 || nIndex < nNumHandles)
{
CRegistryEventRequest* pReq = pThis->m_apRequests[nIndex];
if(pReq->IsActive())
{
pReq->ResetOnChangeHandle();
pReq->Execute(FALSE);
}
}
}
}
// The object signaled to us that it is time to go away
// ====================================================
delete pThis;
return 0;
}
const CLSID CLSID_RegistryEventProvider =
{0xfa77a74e,0xe109,0x11d0,{0xad,0x6e,0x00,0xc0,0x4f,0xd8,0xfd,0xff}};
IUnknown* CRegEventProviderFactory::CreateImpObj()
{
return (IWbemEventProvider*) new CRegEventProvider;
}