|
|
/*++
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>
template <class T> class CLockUnlock { private: T *m_pObj; public: CLockUnlock(T *pObj) : m_pObj(pObj) { if(pObj) pObj->Lock(); } ~CLockUnlock() { if(m_pObj) m_pObj->Unlock(); } };
CRegEventProvider::CRegEventProvider() : m_lRef(0), m_hThread(NULL), m_dwId(NULL), m_hQueueSemaphore(NULL), m_pKeyClass(NULL), m_pValueClass(NULL), m_pTreeClass(NULL), m_pSink(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();
InterlockedDecrement(&lObj); if (m_hThread) CloseHandle(m_hThread); if (m_hQueueSemaphore) CloseHandle(m_hQueueSemaphore); }
STDMETHODIMP CRegEventProvider::QueryInterface(REFIID riid, void** ppv) { *ppv = NULL; if(riid == IID_IWbemEventProvider || riid == IID_IUnknown) { *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) { { CInCritSec ics(&m_cs); // deactivate all event requests
for(int i = 0; i < m_apRequests.GetSize(); i++) { CRegistryEventRequest* pReq = m_apRequests[i];
if(pReq) pReq->ForceDeactivate(); }
m_apRequests.RemoveAll();
if(m_hThread) KillWorker(); } delete this; }
return lRef; }
STDMETHODIMP CRegEventProvider::Initialize(LPWSTR wszUser, long lFlags, LPWSTR wszNamespace, LPWSTR wszLocale, IWbemServices* pNamespace, IWbemContext* pCtx, IWbemProviderInitSink* pSink) { HRESULT hres;
hres = pNamespace->GetObject(REG_KEY_EVENT_CLASS,// strObjectPath
0, // lFlags
pCtx, // pCtx
&m_pKeyClass, // ppObject
NULL); // ppCallResult
if ( SUCCEEDED(hres) ) { hres = pNamespace->GetObject(REG_VALUE_EVENT_CLASS, 0, pCtx, &m_pValueClass, NULL); }
if ( SUCCEEDED(hres) ) { hres = pNamespace->GetObject( REG_TREE_EVENT_CLASS, 0, pCtx, &m_pTreeClass, NULL ); }
if ( SUCCEEDED(hres) ) { m_hQueueSemaphore = CreateSemaphore( NULL, // lpSemaphoreAttributes
0, //lInitialCount
0x7fffffff, // lMaximumCount
NULL); // lpName
if ( m_hQueueSemaphore != NULL ) hres = WBEM_S_NO_ERROR; else hres = WBEM_E_OUT_OF_MEMORY; }
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) { // This is only called after entering the critical section m_cs
int nActiveRequests = m_apRequests.GetSize();
// Search for a similar request
// ============================
// This will not change the number of active requests.
// It will cause the request Id to be served by an existing
// CRegistryEventRequest.
for(int i = 0; i < nActiveRequests; i++) { CRegistryEventRequest* pReq = m_apRequests[i]; // Only active requests are in the array.
if(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);
// If there were no active requests before this one was added
// then we have to start up the worker thread.
if ( nActiveRequests == 0 ) { CreateWorker(); } } return hres; }
STDMETHODIMP CRegEventProvider::CancelQuery(DWORD dwId) { CInCritSec ics(&m_cs);
int nOriginalSize = m_apRequests.GetSize();
// Remove all requests with this Id
// ================================
for(int i = 0; i < m_apRequests.GetSize(); i++) { CRegistryEventRequest* pReq = m_apRequests[i];
// If Deactivate returns WBEM_S_FALSE then the request was not serving
// this id or it is still serving other ids so we leave it in the array.
// If S_OK is returned then the request is no longer serving any ids
// and it is marked as inactive and its resources are released. There
// may still be references to it in the worker thread queue, but the
// worker thread will see that it is inactive and not fire the events.
if (pReq->Deactivate(dwId) == S_OK) { m_apRequests.RemoveAt(i); --i; } }
// If we have cancelled the last subscription then kill the worker thread.
if (nOriginalSize > 0 && m_apRequests.GetSize() == 0) { if(m_hThread) KillWorker(); }
return WBEM_S_NO_ERROR; }
void CRegEventProvider::CreateWorker() { // This is only called while in m_cs
m_hThread = CreateThread(NULL, // lpThreadAttributes
0, // dwStackSize
(LPTHREAD_START_ROUTINE)&CRegEventProvider::Worker, // lpStartAddress
this, // lpParameter
0, // dwCreationFlags
&m_dwId); // lpThreadId
}
void CRegEventProvider::KillWorker() { // When this is called the following is true:
// All waits have been unregistered .
// All thread pool requests have been processed.
// All CRegistryEventRequests in the queue are inactive.
// m_cs has been entered.
// Therefore no other threads will:
// Place events in the queue.
// Modify CRegistryEventRequests in the queue.
// Create or destroy a worker thread.
// So the worker thread will empty the queue of the remaining
// inactive CRegistryEventRequests and then retrieve the null
// event and return.
EnqueueEvent((CRegistryEventRequest*)0);
WaitForSingleObject(m_hThread, // hHandle
INFINITE); // dwMilliseconds
CloseHandle(m_hThread); m_hThread = 0; m_dwId = 0; }
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 = NULL; HRESULT hres = CQueryAnalyser::GetNecessaryQueryForProperty(pExpr, PropName, pPropExpr); if(FAILED(hres)) { return hres; }
if(pPropExpr == NULL) return WBEM_E_FAILED; // 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
// ===========================================================
if ( awsVals.Add(V_BSTR(&Token.vConstValue)) != CWStringArray::no_error) { delete pPropExpr; return WBEM_E_OUT_OF_MEMORY; } } }
delete pPropExpr; return WBEM_S_NO_ERROR; }
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(!wbem_wcsicmp(wszName, L"HKEY_CLASSES_ROOT")) return HKEY_CLASSES_ROOT; /* Disallowed: different semantics for client and server
else if(!wbem_wcsicmp(wszName, L"HKEY_CURRENT_USER")) return HKEY_CURRENT_USER; */ else if(!wbem_wcsicmp(wszName, L"HKEY_LOCAL_MACHINE")) return HKEY_LOCAL_MACHINE; else if(!wbem_wcsicmp(wszName, L"HKEY_USERS")) return HKEY_USERS; else if(!wbem_wcsicmp(wszName, L"HKEY_PERFORMANCE_DATA")) return HKEY_PERFORMANCE_DATA; else if(!wbem_wcsicmp(wszName, L"HKEY_CURRENT_CONFIG")) return HKEY_CURRENT_CONFIG; else if(!wbem_wcsicmp(wszName, L"HKEY_DYN_DATA")) return HKEY_DYN_DATA; else return NULL; }
DWORD CRegEventProvider::Worker(void* p) { CoInitializeEx(0,COINIT_MULTITHREADED);
CRegEventProvider* pThis = (CRegEventProvider*)p;
while(true) { DWORD dwRes = WaitForSingleObject( pThis->m_hQueueSemaphore, // hHandle
INFINITE ); // dwMilliseconds
if ( dwRes != WAIT_OBJECT_0 ) { CoUninitialize(); return dwRes; }
CRegistryEventRequest *pReq = 0;
try { { CInCritSec ics(&(pThis->m_csQueueLock)); pReq = pThis->m_qEventQueue.Dequeue(); }
// If pReq is null then it is a signal for the thread to terminate.
if (pReq) { pReq->ProcessEvent();
// Dequeueing the request doesn't release it.
// If it did then it might be deleted before we had a chance to use it.
// Now we are done with it.
pReq->Release(); } else { break; } } catch( CX_MemoryException& ) { CoUninitialize(); return ERROR_NOT_ENOUGH_MEMORY; } }
CoUninitialize();
return 0; }
void CRegEventProvider::EnqueueEvent(CRegistryEventRequest *pReq) { { CInCritSec ics(&m_csQueueLock); // Placing the request in the queue AddRefs it.
if ( !m_qEventQueue.Enqueue(pReq) ) throw CX_MemoryException(); }
// Tell the worker thread that there is an item to process in the queue.
ReleaseSemaphore(m_hQueueSemaphore, // hSemaphore
1, // lReleaseCount
NULL); // lpPreviousCount
}
VOID CALLBACK CRegEventProvider::EnqueueEvent(PVOID lpParameter, BOOLEAN TimerOrWaitFired) { CRegistryEventRequest *pReq = (CRegistryEventRequest*) lpParameter; CRegEventProvider *pProv = pReq->GetProvider();
pProv->EnqueueEvent(pReq); }
const CLSID CLSID_RegistryEventProvider = {0xfa77a74e,0xe109,0x11d0,{0xad,0x6e,0x00,0xc0,0x4f,0xd8,0xfd,0xff}};
IUnknown* CRegEventProviderFactory::CreateImpObj() { return (IWbemEventProvider*) new CRegEventProvider; }
STDMETHODIMP CRegEventProvider::NewQuery(DWORD dwId, WBEM_WSTR wszLanguage, WBEM_WSTR wszQuery) { HRESULT hres;
CancelQuery(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(!wbem_wcsicmp(pExpr->bsClassName, REG_VALUE_EVENT_CLASS)) { nEventType = e_RegValueChange; } else if(!wbem_wcsicmp(pExpr->bsClassName, REG_KEY_EVENT_CLASS)) { nEventType = e_RegKeyChange; } else if(!wbem_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; }
HKEY* phNew = new HKEY(hHive);
if ( phNew == NULL ) return WBEM_E_OUT_OF_MEMORY; if ( aHives.Add(phNew) < 0 ) return WBEM_E_OUT_OF_MEMORY; } // 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
return hresGlobal; }
STDMETHODIMP CRegEventProvider::AccessCheck(WBEM_CWSTR wszLanguage, WBEM_CWSTR wszQuery, long lSidLength, const BYTE* aSid) { HRESULT hres;
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(), // ThreadHandle
TOKEN_READ, // DesiredAccess
TRUE, // OpenAsSelf
&hToken); // TokenHandle
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(!wbem_wcsicmp(pExpr->bsClassName, REG_VALUE_EVENT_CLASS)) { nEventType = e_RegValueChange; } else if(!wbem_wcsicmp(pExpr->bsClassName, REG_KEY_EVENT_CLASS)) { nEventType = e_RegKeyChange; } else if(!wbem_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; } HKEY* phNew = new HKEY(hHive);
if ( phNew == NULL ) return WBEM_E_OUT_OF_MEMORY; if ( aHives.Add(phNew) < 0 ) return WBEM_E_OUT_OF_MEMORY; } // 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, // hKey
wszKey, // lpSubKey
0, // ulOptions
READ_CONTROL, // samDesired
&hKey); // phkResult
if(lRes) return WBEM_E_NOT_FOUND; CRegCloseMe cm2(hKey);
DWORD dwLen = 0; lRes = RegGetKeySecurity(hKey, // hKey
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, // SecurityInformation
NULL, // pSecurityDescriptor
&dwLen); // lpcbSecurityDescriptor
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, // pSecurityDescriptor
&bAclPresent, // lpbDaclPresent
&pAcl, // pDacl
&bAclDefaulted))// lpbDaclDefaulted
{ 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, st)); 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, // pSecurityDescriptor
hToken, // ClientToken
KEY_NOTIFY, // DesiredAccess
&map, // GenericMapping
ps, // PrivilegeSet
&dwSize, // PrivilegeSetLength
&dwGranted, // GrantedAccess
&bResult); // AccessStatus
if(!bOK || !bResult) return WBEM_E_ACCESS_DENIED; } } }
return WBEM_S_NO_ERROR; }
|