You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
567 lines
15 KiB
567 lines
15 KiB
// NCProvider.cpp : Implementation of CNCProvider
|
|
#include "precomp.h"
|
|
#include "NCProv.h"
|
|
#include "NCProvider.h"
|
|
#include "NCDefs.h"
|
|
#include <list>
|
|
#include "Buffer.h"
|
|
#include "dutils.h"
|
|
#include "NCObjAPI.h"
|
|
#include <Winntsec.h>
|
|
|
|
#define COUNTOF(x) (sizeof(x)/sizeof(x[0]))
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CNCProvider
|
|
|
|
CNCProvider::CNCProvider() :
|
|
m_heventDone(NULL),
|
|
m_heventConnect(NULL),
|
|
m_hPipe( NULL ),
|
|
m_hthreadConnect(NULL),
|
|
m_pProv(NULL)
|
|
{
|
|
InitializeCriticalSection(&m_cs);
|
|
}
|
|
|
|
CNCProvider::~CNCProvider()
|
|
{
|
|
DeleteCriticalSection(&m_cs);
|
|
}
|
|
|
|
void CNCProvider::FinalRelease()
|
|
{
|
|
//
|
|
// do potentially time consuming cleanup in this function rather than
|
|
// DTOR. Reason is that ATL decrements the module ref count before calling
|
|
// the DTOR. This means that a call to DllCanUnloadNow will return TRUE
|
|
// while there is still a call executing in the module. The race condition
|
|
// is that the module could be unloaded while it is still being executed.
|
|
// ATL will call FinalRelease() before decrementing the module refcount
|
|
// making this race condition much smaller. COM addresses this race
|
|
// condition by waiting for a bit to unload the module after returning
|
|
// TRUE. This wait can be controlled by the delay unload param to
|
|
// CoFreeUnusedLibrariesEx(). This allows the call to the last Release()
|
|
// of the COM object to finish, before being unloaded.
|
|
//
|
|
|
|
if ( m_hthreadConnect )
|
|
{
|
|
SetEvent(m_heventDone);
|
|
WaitForSingleObject( m_hthreadConnect, INFINITE );
|
|
CloseHandle(m_hthreadConnect);
|
|
}
|
|
|
|
if (m_heventDone)
|
|
CloseHandle(m_heventDone);
|
|
|
|
delete m_pProv;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CNCProvider::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)
|
|
{
|
|
m_pProv = new CProvInfo;
|
|
|
|
if ( m_pProv == NULL )
|
|
{
|
|
return WBEM_E_OUT_OF_MEMORY;
|
|
}
|
|
|
|
m_pProv->SetNamespace(pNamespace);
|
|
|
|
m_heventDone =
|
|
CreateEvent(
|
|
NULL,
|
|
TRUE,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if ( m_heventDone == NULL )
|
|
{
|
|
return HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
try
|
|
{
|
|
m_strNamespace = pszNamespace;
|
|
}
|
|
catch( _com_error )
|
|
{
|
|
return WBEM_E_OUT_OF_MEMORY;
|
|
}
|
|
|
|
// Tell Windows Management our initialization status.
|
|
return pInitSink->SetStatus( WBEM_S_INITIALIZED, 0 );
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CNCProvider::SetRegistrationObject(
|
|
LONG lFlags,
|
|
IWbemClassObject __RPC_FAR *pProvReg)
|
|
{
|
|
_variant_t vName;
|
|
|
|
if (SUCCEEDED(pProvReg->Get(
|
|
L"Name",
|
|
0,
|
|
&vName,
|
|
NULL,
|
|
NULL)) )
|
|
{
|
|
if ( V_VT(&vName) != VT_BSTR )
|
|
return WBEM_E_INVALID_OBJECT;
|
|
m_strProvider = V_BSTR(&vName);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CNCProvider::AccessCheck(
|
|
/* [in] */ WBEM_CWSTR wszQueryLanguage,
|
|
/* [in] */ WBEM_CWSTR wszQuery,
|
|
/* [in] */ long lSidLength,
|
|
/* [unique][size_is][in] */ const BYTE __RPC_FAR *pSid)
|
|
{
|
|
HRESULT hr;
|
|
|
|
try
|
|
{
|
|
hr =
|
|
m_pProv->AccessCheck(
|
|
wszQueryLanguage,
|
|
wszQuery,
|
|
lSidLength,
|
|
(LPBYTE) pSid);
|
|
}
|
|
catch(...)
|
|
{
|
|
hr = WBEM_E_FAILED;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CNCProvider::NewQuery(
|
|
/* [in] */ DWORD dwID,
|
|
/* [in] */ WBEM_WSTR wszQueryLanguage,
|
|
/* [in] */ WBEM_WSTR wszQuery)
|
|
{
|
|
HRESULT hr;
|
|
|
|
try
|
|
{
|
|
hr = m_pProv->NewQuery(dwID, wszQueryLanguage, wszQuery);
|
|
}
|
|
catch(...)
|
|
{
|
|
hr = WBEM_E_FAILED;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CNCProvider::CancelQuery(
|
|
/* [in] */ DWORD dwID)
|
|
{
|
|
try
|
|
{
|
|
// Get rid of the query item(s).
|
|
m_pProv->CancelQuery(dwID);
|
|
}
|
|
catch(...)
|
|
{
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CNCProvider::ProvideEvents(
|
|
/* [in] */ IWbemObjectSink __RPC_FAR *pSink,
|
|
/* [in] */ long lFlags)
|
|
{
|
|
DWORD dwID;
|
|
IWbemEventSink *pEventSink = NULL;
|
|
HRESULT hr;
|
|
|
|
if (SUCCEEDED(pSink->QueryInterface(
|
|
IID_IWbemEventSink, (LPVOID*) &pEventSink)))
|
|
{
|
|
m_pProv->SetSink(pEventSink);
|
|
pEventSink->Release();
|
|
|
|
if (!m_hthreadConnect)
|
|
{
|
|
m_hthreadConnect =
|
|
CreateThread(
|
|
NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE) ConnectThreadProc,
|
|
this,
|
|
0,
|
|
&dwID);
|
|
}
|
|
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
hr = WBEM_E_FAILED;
|
|
|
|
return hr;
|
|
}
|
|
|
|
DWORD WINAPI CNCProvider::ConnectThreadProc(CNCProvider *pThis)
|
|
{
|
|
DWORD dwRet = ERROR_SUCCESS;
|
|
|
|
if (SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
|
|
{
|
|
try
|
|
{
|
|
pThis->ConnectLoop();
|
|
}
|
|
catch( CX_MemoryException )
|
|
{
|
|
dwRet = ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
CoUninitialize();
|
|
}
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
// ConnectToNewClient(HANDLE, LPOVERLAPPED)
|
|
// This function is called to start an overlapped connect operation.
|
|
// It returns TRUE if an operation is pending or FALSE if the
|
|
// connection has been completed.
|
|
|
|
BOOL CNCProvider::ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo)
|
|
{
|
|
BOOL bConnected,
|
|
bPendingIO = FALSE;
|
|
|
|
// Start an overlapped connection for this pipe instance.
|
|
bConnected = ConnectNamedPipe(hPipe, lpo);
|
|
|
|
// Overlapped ConnectNamedPipe should return zero.
|
|
if (bConnected)
|
|
return FALSE;
|
|
|
|
switch (GetLastError())
|
|
{
|
|
// The overlapped connection in progress.
|
|
case ERROR_IO_PENDING:
|
|
bPendingIO = TRUE;
|
|
break;
|
|
|
|
// Client is already connected, so signal an event.
|
|
case ERROR_PIPE_CONNECTED:
|
|
SetEvent(lpo->hEvent);
|
|
break;
|
|
|
|
// If an error occurs during the connect operation...
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return bPendingIO;
|
|
}
|
|
|
|
#define PIPE_SIZE 64000
|
|
|
|
BOOL CNCProvider::CreateAndConnectInstance(LPOVERLAPPED lpoOverlap, BOOL bFirst)
|
|
{
|
|
SECURITY_ATTRIBUTES sa;
|
|
|
|
sa.nLength = sizeof( SECURITY_ATTRIBUTES );
|
|
sa.bInheritHandle = FALSE;
|
|
|
|
LPWSTR lpwszSD = L"D:" // DACL
|
|
L"(A;;GA;;;SY)" // Allow local system full control
|
|
L"(A;;GRGW;;;LS)" // Allow local service Read/Write
|
|
L"(A;;GRGW;;;NS)"; // Allow network service Read/Write
|
|
|
|
if ( ConvertStringSecurityDescriptorToSecurityDescriptor(
|
|
lpwszSD,
|
|
SDDL_REVISION_1,
|
|
&(sa.lpSecurityDescriptor),
|
|
NULL ) )
|
|
{
|
|
long lFlags = PIPE_ACCESS_DUPLEX | // read/write access
|
|
FILE_FLAG_OVERLAPPED; // overlapped mode
|
|
if( bFirst )
|
|
{
|
|
lFlags |= FILE_FLAG_FIRST_PIPE_INSTANCE;
|
|
}
|
|
|
|
m_hPipe = CreateNamedPipe(
|
|
m_szNamedPipe, // pipe name
|
|
lFlags,
|
|
PIPE_TYPE_MESSAGE | // message-type pipe
|
|
PIPE_READMODE_MESSAGE | // message read mode
|
|
PIPE_WAIT, // blocking mode
|
|
PIPE_UNLIMITED_INSTANCES, // unlimited instances
|
|
PIPE_SIZE, // output buffer size
|
|
PIPE_SIZE, // input buffer size
|
|
0, // client time-out
|
|
&sa ); // security per above
|
|
|
|
if ( INVALID_HANDLE_VALUE == m_hPipe )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Make sure that the pipe is owned by us
|
|
// Call a subroutine to connect to the new client.
|
|
//
|
|
|
|
return ConnectToNewClient(m_hPipe, lpoOverlap);
|
|
/*
|
|
HRESULT hr = WBEM_S_NO_ERROR;
|
|
|
|
SID_IDENTIFIER_AUTHORITY id = SECURITY_NT_AUTHORITY;
|
|
PSID pSidSystem;
|
|
|
|
if (AllocateAndInitializeSid(&id, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0,0,0,0,0,0,&pSidSystem))
|
|
{
|
|
// Create an everyone SID
|
|
PSID pRawSid;
|
|
SID_IDENTIFIER_AUTHORITY id2 = SECURITY_WORLD_SID_AUTHORITY;;
|
|
|
|
if( FALSE == AllocateAndInitializeSid( &id2, 1,0,0,0,0,0,0,0,0, &pRawSid ) )
|
|
{
|
|
FreeSid ( pSidSystem );
|
|
return FALSE;
|
|
}
|
|
|
|
// setup security descriptor with read/write for everyone & owned by local system
|
|
// actual check for valid client is performed in CProvInfo::ClientAccessCheck
|
|
|
|
CNtSid sidWorld( pRawSid );
|
|
FreeSid(pRawSid);
|
|
CNtAce aceWorld(GENERIC_READ | GENERIC_WRITE, ACCESS_ALLOWED_ACE_TYPE, 0, sidWorld);
|
|
|
|
CNtSid sidSystem(pSidSystem);
|
|
FreeSid ( pSidSystem );
|
|
pSidSystem = NULL;
|
|
|
|
CNtAce aceSystem(FULL_CONTROL, ACCESS_ALLOWED_ACE_TYPE, 0, sidSystem);
|
|
|
|
CNtAcl ackl;
|
|
ackl.AddAce(&aceWorld);
|
|
ackl.AddAce(&aceSystem);
|
|
ackl.Resize(CNtAcl::MinimumSize);
|
|
|
|
CNtSecurityDescriptor cSD;
|
|
cSD.SetDacl(&ackl);
|
|
|
|
SECURITY_ATTRIBUTES sa;
|
|
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
sa.bInheritHandle = true;
|
|
sa.lpSecurityDescriptor = (void*)cSD.GetPtr();
|
|
|
|
long lFlags = PIPE_ACCESS_DUPLEX | // read/write access
|
|
FILE_FLAG_OVERLAPPED; // overlapped mode
|
|
if(bFirst)
|
|
lFlags |= FILE_FLAG_FIRST_PIPE_INSTANCE;
|
|
|
|
m_hPipe =
|
|
CreateNamedPipe(
|
|
m_szNamedPipe, // pipe name
|
|
lFlags,
|
|
PIPE_TYPE_MESSAGE | // message-type pipe
|
|
PIPE_READMODE_MESSAGE | // message read mode
|
|
PIPE_WAIT, // blocking mode
|
|
PIPE_UNLIMITED_INSTANCES, // unlimited instances
|
|
PIPE_SIZE, // output buffer size
|
|
PIPE_SIZE, // input buffer size
|
|
0, // client time-out
|
|
&sa); // security per above
|
|
|
|
}
|
|
else // AllocateAndInitSid failed - outta here
|
|
return FALSE;
|
|
|
|
if (m_hPipe == INVALID_HANDLE_VALUE)
|
|
return FALSE;
|
|
|
|
//
|
|
// Make sure that the pipe is owned by us
|
|
// Call a subroutine to connect to the new client.
|
|
|
|
return ConnectToNewClient(m_hPipe, lpoOverlap);
|
|
*/
|
|
}
|
|
|
|
void CNCProvider::ConnectLoop()
|
|
{
|
|
// Init our provider info which will tell our comless providers that
|
|
// we're ready.
|
|
|
|
try
|
|
{
|
|
m_pProv->Init(m_strNamespace, m_strProvider);
|
|
}
|
|
catch( CX_MemoryException )
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_heventConnect =
|
|
CreateEvent(
|
|
NULL, // no security attribute
|
|
TRUE, // manual reset event
|
|
TRUE, // initial state = signaled
|
|
NULL); // unnamed event object
|
|
|
|
//m_pServerPost = new CPostBuffer(this);
|
|
|
|
// TODO: We need to indicate an error here.
|
|
if (!m_heventConnect)
|
|
return;
|
|
|
|
StringCchPrintf(
|
|
m_szNamedPipe,
|
|
256,
|
|
L"\\\\.\\pipe\\" OBJNAME_NAMED_PIPE L"%s%s",
|
|
(LPCWSTR) m_pProv->m_strBaseNamespace,
|
|
(LPCWSTR) m_pProv->m_strBaseName);
|
|
|
|
OVERLAPPED oConnect;
|
|
BOOL bSuccess,
|
|
bPendingIO;
|
|
HANDLE hWait[2] = { m_heventDone, m_heventConnect };
|
|
DWORD dwRet;
|
|
|
|
oConnect.hEvent = m_heventConnect;
|
|
|
|
bPendingIO = CreateAndConnectInstance(&oConnect, TRUE); // first instance
|
|
|
|
while ((dwRet = WaitForMultipleObjectsEx(2, hWait, FALSE, INFINITE, TRUE))
|
|
!= WAIT_OBJECT_0)
|
|
{
|
|
if ( dwRet == WAIT_FAILED )
|
|
{
|
|
break;
|
|
}
|
|
|
|
switch(dwRet)
|
|
{
|
|
case 1:
|
|
{
|
|
if (bPendingIO)
|
|
{
|
|
DWORD dwBytes;
|
|
|
|
bSuccess =
|
|
GetOverlappedResult(
|
|
m_hPipe, // pipe handle
|
|
&oConnect, // OVERLAPPED structure
|
|
&dwBytes, // bytes transferred
|
|
FALSE); // does not wait
|
|
|
|
// TODO: This is an error, but what to do?
|
|
if (!bSuccess)
|
|
break;
|
|
}
|
|
|
|
CPipeClient *pInfo = new CPipeClient(this, m_hPipe);
|
|
|
|
if (pInfo)
|
|
{
|
|
bSuccess =
|
|
ReadFileEx(
|
|
pInfo->m_hPipe,
|
|
pInfo->m_bufferRecv.m_pBuffer,
|
|
pInfo->m_bufferRecv.m_dwSize,
|
|
&pInfo->m_info.overlap,
|
|
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadRoutine);
|
|
|
|
if (!bSuccess)
|
|
DisconnectAndClose(pInfo);
|
|
}
|
|
|
|
bPendingIO = CreateAndConnectInstance(&oConnect, FALSE);
|
|
break;
|
|
}
|
|
|
|
case WAIT_IO_COMPLETION:
|
|
break;
|
|
}
|
|
}
|
|
|
|
CloseHandle(m_hPipe);
|
|
|
|
CloseHandle(m_heventConnect);
|
|
}
|
|
|
|
void CNCProvider::DisconnectAndClose(CClientInfo *pInfo)
|
|
{
|
|
m_pProv->RemoveClient(pInfo);
|
|
}
|
|
|
|
void WINAPI CNCProvider::CompletedReadRoutine(
|
|
DWORD dwErr,
|
|
DWORD nBytesRead,
|
|
LPOVERLAPPED pOverlap)
|
|
{
|
|
CPipeClient *pInfo = ((OLAP_AND_CLIENT*) pOverlap)->pInfo;
|
|
CNCProvider *pThis = pInfo->m_pProvider;
|
|
|
|
#ifndef _DEBUG
|
|
try
|
|
#endif
|
|
{
|
|
#ifndef NO_DECODE
|
|
if (nBytesRead)
|
|
{
|
|
pInfo->PostBuffer(pInfo->m_bufferRecv.m_pBuffer, nBytesRead);
|
|
}
|
|
#endif
|
|
}
|
|
#ifndef _DEBUG
|
|
catch(...)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
try
|
|
{
|
|
// The read operation has finished, so write a response (if no
|
|
// error occurred).
|
|
if (dwErr == 0)
|
|
{
|
|
BOOL bSuccess;
|
|
|
|
bSuccess =
|
|
ReadFileEx(
|
|
pInfo->m_hPipe,
|
|
pInfo->m_bufferRecv.m_pBuffer,
|
|
pInfo->m_bufferRecv.m_dwSize,
|
|
pOverlap,
|
|
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadRoutine);
|
|
|
|
if (!bSuccess)
|
|
pThis->DisconnectAndClose(pInfo);
|
|
}
|
|
else
|
|
pThis->DisconnectAndClose(pInfo);
|
|
}
|
|
catch( CX_MemoryException )
|
|
{
|
|
}
|
|
}
|
|
|