|
|
#include "precomp.h"
#include <stdio.h>
#include <wbemutil.h>
#include <ArrTempl.h>
#include <lmaccess.h>
#include <wbemdisp.h>
#include "ScriptKiller.h"
#include <ErrorObj.h>
#include "script.h"
#include "ClassFac.h"
#include <GroupsForUser.h>
#include <GenUtils.h>
#include <strsafe.h>
#define SCRIPT_PROPNAME_SCRIPT L"ScriptText"
#define SCRIPT_PROPNAME_FILENAME L"ScriptFilename"
#define SCRIPT_PROPNAME_ENGINE L"ScriptingEngine"
#define SCRIPT_PROPNAME_TIMEOUT L"KillTimeout"
#define SCRIPT_EVENTNAME L"TargetEvent"
// uncomment me to remove the WMI script object
// #define NO_DISP_CLASS
#ifdef HOWARDS_DEBUG_CODE
#define NO_DISP_CLASS
#endif // HOWARDS_DEBUG_CODE
HRESULT STDMETHODCALLTYPE CScriptConsumer::XProvider::FindConsumer( IWbemClassObject* pLogicalConsumer, IWbemUnboundObjectSink** ppConsumer) { if (WMIScriptClassFactory::LimitReached()) return RPC_E_DISCONNECTED; CScriptSink* pSink = new CScriptSink(m_pObject->m_pControl); if (!pSink) return WBEM_E_OUT_OF_MEMORY;
HRESULT hres = pSink->Initialize(pLogicalConsumer); if(FAILED(hres)) { delete pSink; *ppConsumer = NULL; return hres; } else return pSink->QueryInterface(IID_IWbemUnboundObjectSink, (void**)ppConsumer); }
void* CScriptConsumer::GetInterface(REFIID riid) { if(riid == IID_IWbemEventConsumerProvider) return &m_XProvider; else return NULL; }
class CScriptSite : public IActiveScriptSite, public IActiveScriptSiteWindow { protected: long m_lRef;
IDispatch* m_pObject; CScriptSink* m_pSink;
HRESULT m_hr; public: CScriptSite(CScriptSink* pSink, IDispatch* pObject); ~CScriptSite();
HRESULT GetScriptHResult() { return m_hr; }
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); virtual HRESULT STDMETHODCALLTYPE GetLCID( /* [out] */ LCID __RPC_FAR *plcid);
virtual HRESULT STDMETHODCALLTYPE GetItemInfo( /* [in] */ LPCOLESTR pstrName, /* [in] */ DWORD dwReturnMask, /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppiunkItem, /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppti);
virtual HRESULT STDMETHODCALLTYPE GetDocVersionString( /* [out] */ BSTR __RPC_FAR *pbstrVersion);
virtual HRESULT STDMETHODCALLTYPE OnScriptTerminate( /* [in] */ const VARIANT __RPC_FAR *pvarResult, /* [in] */ const EXCEPINFO __RPC_FAR *pexcepinfo);
virtual HRESULT STDMETHODCALLTYPE OnStateChange( /* [in] */ SCRIPTSTATE ssScriptState);
virtual HRESULT STDMETHODCALLTYPE OnScriptError( /* [in] */ IActiveScriptError __RPC_FAR *pscripterror);
virtual HRESULT STDMETHODCALLTYPE OnEnterScript( void);
virtual HRESULT STDMETHODCALLTYPE OnLeaveScript( void);
virtual HRESULT STDMETHODCALLTYPE GetWindow( /* [out] */ HWND __RPC_FAR *phwnd);
virtual HRESULT STDMETHODCALLTYPE EnableModeless( /* [in] */ BOOL fEnable);
};
CScriptSite::CScriptSite(CScriptSink* pSink, IDispatch* pObject) : m_lRef(0), m_hr(0) { m_pSink = pSink; m_pSink->AddRef();
m_pObject = pObject; if(m_pObject) m_pObject->AddRef(); }
CScriptSite::~CScriptSite() { if (m_pObject) m_pObject->Release(); if (m_pSink) m_pSink->Release(); }
HRESULT STDMETHODCALLTYPE CScriptSite::QueryInterface(REFIID riid, void** ppv) { if(riid == IID_IUnknown || riid == IID_IActiveScriptSite) *ppv = (IActiveScriptSite*)this; else if(riid == IID_IActiveScriptSiteWindow) *ppv = (IActiveScriptSiteWindow*)this; else return E_NOINTERFACE; ((IUnknown*)*ppv)->AddRef(); return S_OK; }
ULONG STDMETHODCALLTYPE CScriptSite::AddRef() { return InterlockedIncrement(&m_lRef); }
ULONG STDMETHODCALLTYPE CScriptSite::Release() { long lRef = InterlockedDecrement(&m_lRef); if(lRef == 0) delete this; return lRef; }
HRESULT STDMETHODCALLTYPE CScriptSite::GetLCID( /* [out] */ LCID __RPC_FAR *plcid) { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE CScriptSite::GetItemInfo( /* [in] */ LPCOLESTR pstrName, /* [in] */ DWORD dwReturnMask, /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppiunkItem, /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppti) { if(wbem_wcsicmp(pstrName, SCRIPT_EVENTNAME)) return TYPE_E_ELEMENTNOTFOUND; if(ppti) *ppti = NULL; if(ppiunkItem) *ppiunkItem = NULL;
if(dwReturnMask & SCRIPTINFO_IUNKNOWN) { if(ppiunkItem == NULL) return E_POINTER; m_pObject->QueryInterface(IID_IUnknown, (void**)ppiunkItem); } return S_OK; }
HRESULT STDMETHODCALLTYPE CScriptSite::GetDocVersionString( /* [out] */ BSTR __RPC_FAR *pbstrVersion) { return E_NOTIMPL;}
HRESULT STDMETHODCALLTYPE CScriptSite::OnScriptTerminate( /* [in] */ const VARIANT __RPC_FAR *pvarResult, /* [in] */ const EXCEPINFO __RPC_FAR *pexcepinfo) { return S_OK; }
HRESULT STDMETHODCALLTYPE CScriptSite::OnStateChange( /* [in] */ SCRIPTSTATE ssScriptState) { return S_OK;}
HRESULT STDMETHODCALLTYPE CScriptSite::OnScriptError( /* [in] */ IActiveScriptError __RPC_FAR *pscripterror) { HRESULT hres; EXCEPINFO ei; hres = pscripterror->GetExceptionInfo(&ei); if(SUCCEEDED(hres)) { if (ei.bstrSource) { m_pSink->m_wsErrorMessage = ei.bstrSource; m_pSink->m_wsErrorMessage += L": "; }
if (ei.bstrDescription) m_pSink->m_wsErrorMessage += ei.bstrDescription; else m_pSink->m_wsErrorMessage += L"unknown";
if ((ei.wCode != 0) && (ei.wCode >= 1000)) m_hr = WBEM_E_FAILED; else m_hr = ei.scode; } DWORD cookie; ULONG lineNo = 0; LONG charPos = 0; hres = pscripterror->GetSourcePosition(&cookie, &lineNo, &charPos);
// we will construct an error message of the form:
// filename.vbs (3,15)
if(SUCCEEDED(hres)) { if (m_pSink->m_wsScriptFileName.Length() > 0) m_pSink->m_wsErrorLine = m_pSink->m_wsScriptFileName; else m_pSink->m_wsErrorLine = SCRIPT_PROPNAME_SCRIPT;
// if the sprintf fails for some reason, no problem we concat a null sting.
WCHAR buf[256] = L"\0"; // experimentation shows that the line/pos appear to be zero based: add one.
// swprintf(buf, L" (%u,%d)", lineNo +1, charPos +1);
StringCchPrintfW(buf, 256, L" (%u,%d)", lineNo +1, charPos +1); m_pSink->m_wsErrorLine += buf; }
return S_OK; }
HRESULT STDMETHODCALLTYPE CScriptSite::OnEnterScript( void) { return S_OK;}
HRESULT STDMETHODCALLTYPE CScriptSite::OnLeaveScript( void) { return S_OK;}
HRESULT STDMETHODCALLTYPE CScriptSite::GetWindow( /* [out] */ HWND __RPC_FAR *phwnd) { *phwnd = NULL; return S_OK; }
HRESULT STDMETHODCALLTYPE CScriptSite::EnableModeless( /* [in] */ BOOL fEnable) {return S_OK;}
CScriptSink::~CScriptSink() { if(m_pEngineFac) m_pEngineFac->Release(); if (m_pErrorObj) m_pErrorObj->Release();
}
HRESULT CScriptSink::Initialize(IWbemClassObject* pLogicalConsumer) { VARIANT v; VariantInit(&v);
// this is actually a pointer to a static object
// if it fails, something is Very, Very Wrong.
m_pErrorObj = ErrorObj::GetErrorObj(); if (!m_pErrorObj) return WBEM_E_CRITICAL_ERROR; BSTR propName; propName = SysAllocString(L"CreatorSID"); if (!propName) return WBEM_E_OUT_OF_MEMORY; CSysFreeMe freeName(propName);
if (SUCCEEDED(pLogicalConsumer->Get(propName, 0, &v, NULL, NULL))) { HRESULT hDebug; long ubound; hDebug = SafeArrayGetUBound(V_ARRAY(&v), 1, &ubound);
PVOID pVoid; hDebug = SafeArrayAccessData(V_ARRAY(&v), &pVoid);
if(SUCCEEDED(hDebug)) { m_pSidCreator = new BYTE[ubound +1]; if (m_pSidCreator) memcpy(m_pSidCreator, pVoid, ubound + 1); else { SafeArrayUnaccessData(V_ARRAY(&v)); return WBEM_E_OUT_OF_MEMORY; }
SafeArrayUnaccessData(V_ARRAY(&v)); } } else { return WBEM_E_OUT_OF_MEMORY; } // Get the information
// ===================
HRESULT hres; VariantInit(&v);
hres = pLogicalConsumer->Get(SCRIPT_PROPNAME_ENGINE, 0, &v, NULL, NULL); if(FAILED(hres) || V_VT(&v) != VT_BSTR) { m_pErrorObj->ReportError(SCRIPT_PROPNAME_ENGINE, NULL, NULL, WBEM_E_INVALID_PARAMETER, true); return WBEM_E_INVALID_PARAMETER; } WString wsEngine = V_BSTR(&v); VariantClear(&v);
hres = pLogicalConsumer->Get(SCRIPT_PROPNAME_TIMEOUT, 0, &v, NULL, NULL); if(V_VT(&v) == VT_I4) m_dwKillTimeout = V_I4(&v); else m_dwKillTimeout = 0; VariantClear(&v);
hres = pLogicalConsumer->Get(SCRIPT_PROPNAME_SCRIPT, 0, &v, NULL, NULL); if (SUCCEEDED(hres)) { if (V_VT(&v) == VT_BSTR) { m_wsScript = V_BSTR(&v); VariantClear(&v); } else // try the script file name approach
{ hres = pLogicalConsumer->Get(SCRIPT_PROPNAME_FILENAME, 0, &v, NULL, NULL); if (SUCCEEDED(hres) && (V_VT(&v) == VT_BSTR)) { m_wsScriptFileName = V_BSTR(&v); VariantClear(&v); } else { m_pErrorObj->ReportError(L"Initialize", L"ScriptText, ScriptFilename", NULL, WBEM_E_ILLEGAL_NULL, true); return WBEM_E_INVALID_PARAMETER; } } } else return WBEM_E_INVALID_PARAMETER;
// Get the CLSID
// =============
CLSID clsid; if (wsEngine.Length() == 0) hres = WBEM_E_INVALID_PARAMETER; else hres = CLSIDFromProgID((LPCWSTR)wsEngine, &clsid);
if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Scripting engine '%S' not found: %X\n", (LPCWSTR)wsEngine, hres)); m_pErrorObj->ReportError(L"Initialize", (WCHAR *)wsEngine, NULL, hres, true); return hres; }
hres = CoGetClassObject(clsid, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, NULL, IID_IClassFactory, (void**)&m_pEngineFac); if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Unable to create scripting engine %S: %X\n", (LPCWSTR)wsEngine, hres)); m_pErrorObj->ReportError(L"Initialize", (WCHAR*)wsEngine, NULL, hres, true); return hres; }
return S_OK; }
// runs the script contained in the script text
HRESULT CScriptSink::RunScriptText(IWbemClassObject *pObj) { HRESULT hres = S_OK;
WMIScriptClassFactory::IncrementScriptsRun(); IActiveScript* pScript; hres = m_pEngineFac->CreateInstance(NULL, IID_IActiveScript, (void**)&pScript); if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Unable to create a script. Error code %X\n", hres)); return hres; }
IActiveScriptParse* pParse; hres = pScript->QueryInterface(IID_IActiveScriptParse, (void**)&pParse); if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Scripting engine does not support " "parsing!\n")); pScript->Release(); return hres; }
IDispatch* pDObject; #ifdef NO_DISP_CLASS
pDObject = NULL; #else
IBindCtx *pbc = NULL;; IMoniker *pMk = NULL; ULONG chEaten = 0; if(FAILED(hres = CreateBindCtx(0, &pbc))) { ERRORTRACE((LOG_ESS, "Unable to Create IBindCtx: 0x%X\n", hres)); pScript->Release(); return hres; }
if(FAILED(hres = pbc->RegisterObjectParam(L"WmiObject", pObj))) { ERRORTRACE((LOG_ESS, "Unable to Register IBindCtx: 0x%X\n", hres)); pScript->Release(); return hres; }
if(FAILED(hres = MkParseDisplayName(pbc, L"winmgmts:", &chEaten, &pMk))) { ERRORTRACE((LOG_ESS, "Unable to MkParseDisplayName: 0x%X\n", hres)); pScript->Release(); return hres; }
if(FAILED(hres = pMk->BindToObject(pbc, 0, IID_ISWbemObject, (void **)&pDObject))) { ERRORTRACE((LOG_ESS, "Unable to BindToObject: 0x%X\n", hres)); pScript->Release(); return hres; }
pMk->Release(); pbc->Release(); #endif
CScriptSite* pSite = new CScriptSite(this, pDObject); pSite->AddRef();
#ifndef NO_DISP_CLASS
if(pDObject) pDObject->Release(); #endif
hres = pScript->SetScriptSite(pSite);
hres = pParse->InitNew(); if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Failed to initialize script(InitNew): %X\n", hres)); pSite->Release(); pScript->Release(); pParse->Release(); return hres; }
#ifndef NO_DISP_CLASS
hres = pScript->AddNamedItem(SCRIPT_EVENTNAME, SCRIPTITEM_ISVISIBLE | SCRIPTITEM_NOCODE);
if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Failed to add named item: %X\n", hres)); pSite->Release(); pScript->Release(); pParse->Release(); return hres; } #endif
EXCEPINFO ei; hres = pParse->ParseScriptText( (LPCWSTR)m_wsScript, NULL, NULL, NULL, 0, 0, 0, NULL, &ei);
if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Failed to parse script. Error code %X\n" "Scripting engine says: %S\n", hres, (LPCWSTR)m_wsErrorMessage)); m_pErrorObj->ReportError(L"ParseScriptText", m_wsErrorLine, m_wsErrorMessage, hres, true); pSite->Release(); pScript->Release(); pParse->Release(); return hres; } pParse->Release();
if (m_dwKillTimeout) { FILETIME now; GetSystemTimeAsFileTime(&now); WAYCOOL_FILETIME expires(now); expires.AddSeconds(m_dwKillTimeout);
SCRIPTTHREADID threadID; hres = pScript->GetScriptThreadID(GetCurrentThreadId(), &threadID); if (SUCCEEDED(hres)) g_scriptKillerTimer.ScheduleAssassination(pScript, expires, threadID);
/************
Doing it in the stream. Probably don't need to. LPSTREAM pStream; if (SUCCEEDED(hres) && SUCCEEDED(CoMarshalInterThreadInterfaceInStream(IID_IActiveScript, pScript, &pStream))) { g_scriptKillerTimer.ScheduleAssassination(pStream, expires, threadID); } ***************/ } hres = pScript->SetScriptState(SCRIPTSTATE_CONNECTED); if(FAILED(hres)) { ERRORTRACE((LOG_ESS, "Failed to execute script. Error code 0x%X\n" "Scripting engine says: %S\n", hres, (LPCWSTR)m_wsErrorMessage));
m_pErrorObj->ReportError(L"SetScriptState(SCRIPTSTATE_CONNECTED)", m_wsErrorLine, m_wsErrorMessage, hres, true); } else if (FAILED(pSite->GetScriptHResult())) { hres = pSite->GetScriptHResult(); ERRORTRACE((LOG_ESS, "Error in script execution. Error code 0x%X\n" "Scripting engine says: %S\n", hres, (LPCWSTR)m_wsErrorMessage)); m_pErrorObj->ReportError(L"SetScriptState(SCRIPTSTATE_CONNECTED)", m_wsErrorLine, m_wsErrorMessage, hres, true);
} pScript->Close();
pScript->Release(); pSite->Release();
return hres; }
HRESULT CScriptSink::RunScriptFile(IWbemClassObject *pObj) { HRESULT hr = WBEM_E_INVALID_PARAMETER;
if (m_wsScriptFileName.Length()) { HANDLE hFile = CreateFileW((LPWSTR)m_wsScriptFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE) { DWORD fSize; if (0xFFFFFFFF != (fSize = GetFileSize(hFile, &fSize))) { char* pBuf = new char[fSize +2]; if (!pBuf) hr = WBEM_E_OUT_OF_MEMORY; else { ZeroMemory(pBuf, fSize+2); DWORD bitsRead;
if (ReadFile(hFile, pBuf, fSize, &bitsRead, NULL)) { hr = WBEM_S_NO_ERROR;
const WCHAR ByteOrderMark = L'\xFEFF'; // determine whether this is a unicode file
if (((WCHAR*)pBuf)[0] == ByteOrderMark) m_wsScript.BindPtr((WCHAR*)pBuf); else { int length = strlen(pBuf) +1;
// not unicode, do the conversion
WCHAR* pWideBuf = new WCHAR[length]; if (!pWideBuf) hr = WBEM_E_OUT_OF_MEMORY; else { if (!MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED, pBuf, length, pWideBuf, length)) { ERRORTRACE((LOG_ESS, "Script: cannot convert %s, 0x%08X\n", pBuf, GetLastError())); hr = WBEM_E_FAILED; } else m_wsScript.BindPtr(pWideBuf); }
// delete the old buffer - we saved a copy
delete[] pBuf; } if (SUCCEEDED(hr)) hr = RunScriptText(pObj); } else { ERRORTRACE((LOG_ESS, "Script: Cannot read %S, 0x%X\n", (LPWSTR)m_wsScriptFileName, GetLastError())); delete[] pBuf; hr = WBEM_E_FAILED; }
} }
CloseHandle(hFile); } else ERRORTRACE((LOG_ESS, "Script: Cannot Open %S, 0x%X\n", (LPWSTR)m_wsScriptFileName, GetLastError())); }
return hr; }
HRESULT STDMETHODCALLTYPE CScriptSink::XSink::IndicateToConsumer( IWbemClassObject* pLogicalConsumer, long lNumObjects, IWbemClassObject** apObjects) { PSID pSidSystem; SID_IDENTIFIER_AUTHORITY id = SECURITY_NT_AUTHORITY;
if (AllocateAndInitializeSid(&id, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0,0,0,0,0,0,&pSidSystem)) { // guilty until proven innocent
HRESULT hr = WBEM_E_ACCESS_DENIED;
// check to see if sid is either Local System or an admin of some sort...
if ((EqualSid(pSidSystem, m_pObject->m_pSidCreator)) || (S_OK == IsUserAdministrator(m_pObject->m_pSidCreator))) hr = WBEM_S_NO_ERROR; // We're done with this
FreeSid(pSidSystem);
if (FAILED(hr)) return hr; } else return WBEM_E_OUT_OF_MEMORY;
if (WMIScriptClassFactory::LimitReached()) return RPC_E_DISCONNECTED; HRESULT hrOutter = WBEM_S_NO_ERROR;
for(int i = 0; i < lNumObjects; i++) { HRESULT hrInner; if (m_pObject->m_wsScript.Length()) hrInner = m_pObject->RunScriptText(apObjects[i]); else if (m_pObject->m_wsScriptFileName.Length()) hrInner = m_pObject->RunScriptFile(apObjects[i]); else { m_pObject->m_pErrorObj->ReportError(L"IndicateToConsumer", L"ScriptText, ScriptFilename", NULL, WBEM_E_ILLEGAL_NULL, true); return WBEM_E_INVALID_PARAMETER; }
if (FAILED(hrInner)) { m_pObject->m_pErrorObj->ReportError(L"IndicateToConsumer", m_pObject->m_wsErrorLine, m_pObject->m_wsErrorMessage, hrInner, true); hrOutter = hrInner; // m_pObject->RaiseErrorStatus();
} }
return hrOutter; }
void* CScriptSink::GetInterface(REFIID riid) { if(riid == IID_IWbemUnboundObjectSink) return &m_XSink; else return NULL; }
|