#include "precomp.h" #include #include #include #include "ProcKiller.h" #include #include #include #include #include "cmdline.h" #include #include #include #include #define CMDLINE_PROPNAME_EXECUTABLE L"ExecutablePath" #define CMDLINE_PROPNAME_COMMANDLINE L"CommandLineTemplate" #define CMDLINE_PROPNAME_USEDEFAULTERRORMODE L"UseDefaultErrorMode" #define CMDLINE_PROPNAME_CREATENEWCONSOLE L"CreateNewConsole" #define CMDLINE_PROPNAME_CREATENEWPROCESSGROUP L"CreateNewProcessGroup" #define CMDLINE_PROPNAME_CREATESEPARATEWOWVDM L"CreateSeparateWowVdm" #define CMDLINE_PROPNAME_CREATESHAREDWOWVDM L"CreateSharedWowVdm" #define CMDLINE_PROPNAME_PRIORITY L"Priority" #define CMDLINE_PROPNAME_WORKINGDIRECTORY L"WorkingDirectory" #define CMDLINE_PROPNAME_DESKTOP L"DesktopName" #define CMDLINE_PROPNAME_TITLE L"WindowTitle" #define CMDLINE_PROPNAME_X L"XCoordinate" #define CMDLINE_PROPNAME_Y L"YCoordinate" #define CMDLINE_PROPNAME_XSIZE L"XSize" #define CMDLINE_PROPNAME_YSIZE L"YSize" #define CMDLINE_PROPNAME_XCOUNTCHARS L"XNumCharacters" #define CMDLINE_PROPNAME_YCOUNTCHARS L"YNumCharacters" #define CMDLINE_PROPNAME_FILLATTRIBUTE L"FillAttribute" #define CMDLINE_PROPNAME_SHOWWINDOW L"ShowWindowCommand" #define CMDLINE_PROPNAME_FORCEON L"ForceOnFeedback" #define CMDLINE_PROPNAME_FORCEOFF L"ForceOffFeedback" #define CMDLINE_PROPNAME_INTERACTIVE L"RunInteractively" #define CMDLINE_PROPNAME_KILLTIMEOUT L"KillTimeout" #define CMDLINE_PROPNAME_CREATORSID L"CreatorSid" HRESULT STDMETHODCALLTYPE CCommandLineConsumer::XProvider::FindConsumer( IWbemClassObject* pLogicalConsumer, IWbemUnboundObjectSink** ppConsumer) { CCommandLineSink* pSink = new CCommandLineSink(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* CCommandLineConsumer::GetInterface(REFIID riid) { if(riid == IID_IWbemEventConsumerProvider) return &m_XProvider; else return NULL; } HRESULT CCommandLineSink::Initialize(IWbemClassObject* pLogicalConsumer) { // 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; // Get the information // =================== HRESULT hres; VARIANT v; VariantInit(&v); // only one of the pair Executable & commandLine may be null // this var counts... int nNulls = 0; hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_EXECUTABLE, 0, &v, NULL, NULL); if(FAILED(hres) || V_VT(&v) != VT_BSTR) { m_wsExecutable = L""; nNulls++; } else m_wsExecutable = V_BSTR(&v); VariantClear(&v); hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_COMMANDLINE, 0, &v, NULL, NULL); if(FAILED(hres) || V_VT(&v) != VT_BSTR) { m_CommandLine.SetTemplate(L""); nNulls++; } else m_CommandLine.SetTemplate(V_BSTR(&v)); VariantClear(&v); if (nNulls > 1) return WBEM_E_INVALID_PARAMETER; hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_WORKINGDIRECTORY, 0, &v, NULL, NULL); if(SUCCEEDED(hres) && V_VT(&v) == VT_BSTR) m_wsWorkingDirectory = V_BSTR(&v); VariantClear(&v); hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_DESKTOP, 0, &v, NULL, NULL); if(SUCCEEDED(hres) && V_VT(&v) == VT_BSTR) m_wsDesktop = V_BSTR(&v); VariantClear(&v); hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_TITLE, 0, &v, NULL, NULL); if(FAILED(hres) || V_VT(&v) != VT_BSTR) m_title.SetTemplate(L""); else m_title.SetTemplate(V_BSTR(&v)); VariantClear(&v); hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_INTERACTIVE, 0, &v, NULL, NULL); if(V_VT(&v) == VT_BOOL && V_BOOL(&v) != VARIANT_FALSE) m_bInteractive = TRUE; else m_bInteractive = FALSE; m_dwCreationFlags = 0; hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_USEDEFAULTERRORMODE, 0, &v, NULL, NULL); if(V_VT(&v) == VT_BOOL && V_BOOL(&v) != VARIANT_FALSE) m_dwCreationFlags |= CREATE_DEFAULT_ERROR_MODE; hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_CREATENEWCONSOLE, 0, &v, NULL, NULL); if(V_VT(&v) == VT_BOOL && V_BOOL(&v) != VARIANT_FALSE) m_dwCreationFlags |= CREATE_NEW_CONSOLE; hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_CREATENEWPROCESSGROUP, 0, &v, NULL, NULL); if(V_VT(&v) == VT_BOOL && V_BOOL(&v) != VARIANT_FALSE) m_dwCreationFlags |= CREATE_NEW_PROCESS_GROUP; hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_CREATESEPARATEWOWVDM, 0, &v, NULL, NULL); if(V_VT(&v) == VT_BOOL && V_BOOL(&v) != VARIANT_FALSE) m_dwCreationFlags |= CREATE_SEPARATE_WOW_VDM; hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_CREATESHAREDWOWVDM, 0, &v, NULL, NULL); if(V_VT(&v) == VT_BOOL && V_BOOL(&v) != VARIANT_FALSE) m_dwCreationFlags |= CREATE_SHARED_WOW_VDM; hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_PRIORITY, 0, &v, NULL, NULL); if(V_VT(&v) == VT_I4) { if(V_I4(&v) == HIGH_PRIORITY_CLASS || V_I4(&v) == IDLE_PRIORITY_CLASS || V_I4(&v) == NORMAL_PRIORITY_CLASS || V_I4(&v) == REALTIME_PRIORITY_CLASS) { m_dwCreationFlags |= V_I4(&v); } else return WBEM_E_INVALID_PARAMETER; } m_dwStartFlags = 0; hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_X, 0, &v, NULL, NULL); if(V_VT(&v) == VT_I4) { m_dwX = V_I4(&v); m_dwStartFlags |= STARTF_USEPOSITION; } hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_Y, 0, &v, NULL, NULL); if(V_VT(&v) == VT_I4) { m_dwY = V_I4(&v); m_dwStartFlags |= STARTF_USEPOSITION; } hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_XSIZE, 0, &v, NULL, NULL); if(V_VT(&v) == VT_I4) { m_dwXSize = V_I4(&v); m_dwStartFlags |= STARTF_USESIZE; } hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_YSIZE, 0, &v, NULL, NULL); if(V_VT(&v) == VT_I4) { m_dwYSize = V_I4(&v); m_dwStartFlags |= STARTF_USESIZE; } hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_XCOUNTCHARS, 0, &v, NULL, NULL); if(V_VT(&v) == VT_I4) { m_dwXNumCharacters = V_I4(&v); m_dwStartFlags |= STARTF_USECOUNTCHARS; } hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_YCOUNTCHARS, 0, &v, NULL, NULL); if(V_VT(&v) == VT_I4) { m_dwYNumCharacters = V_I4(&v); m_dwStartFlags |= STARTF_USECOUNTCHARS; } hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_FILLATTRIBUTE, 0, &v, NULL, NULL); if(V_VT(&v) == VT_I4) { m_dwFillAttribute = V_I4(&v); m_dwStartFlags |= STARTF_USEFILLATTRIBUTE; } hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_SHOWWINDOW, 0, &v, NULL, NULL); if(V_VT(&v) == VT_I4) { m_dwShowWindow = V_I4(&v); m_dwStartFlags |= STARTF_USESHOWWINDOW; } hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_FORCEON, 0, &v, NULL, NULL); if(V_VT(&v) == VT_BOOL && V_BOOL(&v) != VARIANT_FALSE) m_dwStartFlags |= STARTF_FORCEONFEEDBACK; hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_FORCEOFF, 0, &v, NULL, NULL); if(V_VT(&v) == VT_BOOL && V_BOOL(&v) != VARIANT_FALSE) m_dwStartFlags |= STARTF_FORCEOFFFEEDBACK; hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_KILLTIMEOUT, 0, &v, NULL, NULL); if(V_VT(&v) == VT_I4) m_dwKillTimeout = V_I4(&v); else m_dwKillTimeout = 0; VariantClear(&v); hres = pLogicalConsumer->Get(CMDLINE_PROPNAME_CREATORSID, 0, &v, NULL, NULL); if (SUCCEEDED(hres)) { HRESULT hDebug; long ubound; hDebug = SafeArrayGetUBound(V_ARRAY(&v), 1, &ubound); if(SUCCEEDED(hDebug)) { 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 hres = WBEM_E_OUT_OF_MEMORY; SafeArrayUnaccessData(V_ARRAY(&v)); } } } else { ERRORTRACE((LOG_ESS, "Command Line Consumer could not retrieve creator sid (0x%08X)\n",hres)); return hres; } return hres; } BOOL IsInteractive(HWINSTA hWinsta) { USEROBJECTFLAGS uof; DWORD dwLen; BOOL bRes = GetUserObjectInformation(hWinsta, UOI_FLAGS, (void*)&uof, sizeof(uof), &dwLen); if(!bRes) return FALSE; return ((uof.dwFlags & WSF_VISIBLE) != 0); } BOOL WinstaEnumProc(LPTSTR szWindowStation, LPARAM lParam) { WString* pws = (WString*)lParam; HWINSTA hWinsta = OpenWindowStation(szWindowStation, FALSE, WINSTA_ENUMERATE | WINSTA_ENUMDESKTOPS | WINSTA_READATTRIBUTES); if(hWinsta == NULL) return TRUE; if(IsInteractive(hWinsta)) { *pws = szWindowStation; } CloseWindowStation(hWinsta); return TRUE; } BOOL GetInteractiveWinstation(WString& wsName) { wsName.Empty(); BOOL bRes = EnumWindowStations(WinstaEnumProc, (LPARAM)&wsName); return bRes; } HRESULT CCommandLineSink::FindInteractiveInfo() { BOOL bRes = GetInteractiveWinstation(m_wsWindowStation); if(!bRes) { return WBEM_E_FAILED; } if(m_wsWindowStation.Length() == 0) return WBEM_E_NOT_FOUND; return WBEM_S_NO_ERROR; } HRESULT GetSidUse(PSID pSid, SID_NAME_USE& use) { DWORD dwNameLen = 0; DWORD dwDomainLen = 0; LPWSTR pUser = 0; LPWSTR pDomain = 0; use = SidTypeInvalid; // Do the first lookup to get the buffer sizes required. // ===================================================== BOOL bRes = LookupAccountSidW( NULL, pSid, pUser, &dwNameLen, pDomain, &dwDomainLen, &use ); DWORD dwLastErr = GetLastError(); if (dwLastErr != ERROR_INSUFFICIENT_BUFFER) { return WBEM_E_FAILED; } // Allocate the required buffers and look them up again. // ===================================================== pUser = new wchar_t[dwNameLen + 1]; if (!pUser) return WBEM_E_OUT_OF_MEMORY; pDomain = new wchar_t[dwDomainLen + 1]; if (!pDomain) { delete pUser; return WBEM_E_OUT_OF_MEMORY; } bRes = LookupAccountSidW( NULL, pSid, pUser, &dwNameLen, pDomain, &dwDomainLen, &use ); delete [] pUser; delete [] pDomain; if (bRes) return WBEM_S_NO_ERROR; else return WBEM_E_FAILED; } bool GetLoggedOnUserViaTS( CNtSid& sidLoggedOnUser) { bool fRet = false; bool fCont = true; PWTS_SESSION_INFO psesinfo = NULL; DWORD dwSessions = 0; LPWSTR wstrUserName = NULL; LPWSTR wstrDomainName = NULL; LPWSTR wstrWinstaName = NULL; DWORD dwSize = 0L; try { if(!(::WTSEnumerateSessions( WTS_CURRENT_SERVER_HANDLE, 0, 1, &psesinfo, &dwSessions) && psesinfo)) { fCont = false; } if(fCont) { for(int j = 0; j < dwSessions && !fRet; j++, fCont = true) { if(psesinfo[j].State != WTSActive) { fCont = false; } if(fCont) { if(!(::WTSQuerySessionInformation( WTS_CURRENT_SERVER_HANDLE, psesinfo[j].SessionId, WTSUserName, &wstrUserName, &dwSize) && wstrUserName)) { fCont = false; } } if(fCont) { if(!(::WTSQuerySessionInformation( WTS_CURRENT_SERVER_HANDLE, psesinfo[j].SessionId, WTSDomainName, &wstrDomainName, &dwSize) && wstrDomainName)) { fCont = false; } } if(fCont) { if(!(::WTSQuerySessionInformation( WTS_CURRENT_SERVER_HANDLE, psesinfo[j].SessionId, WTSWinStationName, &wstrWinstaName, &dwSize) && wstrWinstaName)) { fCont = false; } } if(fCont) { if(wbem_wcsicmp(wstrWinstaName, L"Console") != 0) { fCont = false; } } if(fCont) { WCHAR buf[256]; DWORD bufSize = 255; if (GetComputerNameW(buf, &bufSize)) { DWORD nRet = 0; // if it's a local account, we look it up locally // else we find a DC to look up. WCHAR* pServer = NULL; if (0 == wbem_wcsicmp(buf, wstrDomainName)) pServer = NULL; else nRet = NetGetDCName(NULL, wstrDomainName, (LPBYTE *)&pServer); if (nRet == 0) { // That establishes that this user // is associated with the interactive // desktop. CNtSid sidInteractive(wstrUserName, pServer); if(sidInteractive.GetStatus() == CNtSid::NoError) { sidLoggedOnUser = sidInteractive; fRet = true; } if (pServer) NetApiBufferFree(pServer); } else fRet = false; } } if(wstrUserName) { WTSFreeMemory(wstrUserName); wstrUserName = NULL; } if(wstrDomainName) { WTSFreeMemory(wstrDomainName); wstrDomainName = NULL; } if(wstrWinstaName) { WTSFreeMemory(wstrWinstaName); wstrWinstaName = NULL; } } if (psesinfo) WTSFreeMemory(psesinfo); } } catch(...) { if(wstrUserName) { WTSFreeMemory(wstrUserName); wstrUserName = NULL; } if(wstrDomainName) { WTSFreeMemory(wstrDomainName); wstrDomainName = NULL; } if(wstrWinstaName) { WTSFreeMemory(wstrWinstaName); wstrWinstaName = NULL; } if (psesinfo) WTSFreeMemory(psesinfo); fRet = false; } return fRet; } HRESULT STDMETHODCALLTYPE CCommandLineSink::XSink::CreateProcessNT(WCHAR* pCommandLine, WCHAR* pTitle, PROCESS_INFORMATION& pi, FILETIME& now) { HRESULT hr = WBEM_S_NO_ERROR; WCHAR* pDesktop = NULL; WString wsDesktop; if(m_pObject->m_bInteractive) { if(FAILED(m_pObject->FindInteractiveInfo())) { ERRORTRACE((LOG_ESS, "No interactive window station found!\n")); return WBEM_E_FAILED; } wsDesktop = m_pObject->m_wsWindowStation; wsDesktop += L"\\Default"; pDesktop = (wchar_t*)wsDesktop; CNtSid user; if (!GetLoggedOnUserViaTS(user)) { ERRORTRACE((LOG_ESS, "Could not determine logged on user\n")); return WBEM_E_FAILED; } SID_NAME_USE use; if (FAILED(hr =GetSidUse(m_pObject->m_pSidCreator, use))) return hr; if (use == SidTypeUser) { if (!EqualSid(m_pObject->m_pSidCreator, user.GetPtr())) { ERRORTRACE((LOG_ESS, "Command line event consumer will only run interactively\non a workstation that the creator is logged into.\n")); m_pObject->m_pErrorObj->ReportError(L"AccessCheck", L"RunInteractively", NULL, WBEM_E_ACCESS_DENIED, true); return WBEM_E_ACCESS_DENIED; } // else we're fine continue. DEBUGTRACE((LOG_ESS, "User and creator are one in the same\n")); } else { if (0 != IsUserInGroup(user.GetPtr(), m_pObject->m_pSidCreator)) { ERRORTRACE((LOG_ESS, "Command line event consumer will only run interactively\non a workstation that the creator is logged into.\n")); m_pObject->m_pErrorObj->ReportError(L"AccessCheck", L"RunInteractively", NULL, WBEM_E_ACCESS_DENIED, true); return WBEM_E_ACCESS_DENIED; } else { DEBUGTRACE((LOG_ESS, "User is in the group!\n")); } } } WCHAR* szApplicationName = (m_pObject->m_wsExecutable.Length() == 0) ? NULL : ((wchar_t*)m_pObject->m_wsExecutable); WCHAR* szWorkingDirectory = (m_pObject->m_wsWorkingDirectory.Length() == 0) ? NULL : ((wchar_t*)m_pObject->m_wsWorkingDirectory); struct _STARTUPINFOW si; si.cb = sizeof(si); si.lpReserved = NULL; si.cbReserved2 = 0; si.lpReserved2 = NULL; si.lpDesktop = pDesktop; si.lpTitle = pTitle; si.dwX = m_pObject->m_dwX; si.dwY = m_pObject->m_dwY; si.dwXSize = m_pObject->m_dwXSize; si.dwYSize = m_pObject->m_dwYSize; si.dwXCountChars = m_pObject->m_dwXNumCharacters; si.dwYCountChars = m_pObject->m_dwYNumCharacters; si.dwFillAttribute = m_pObject->m_dwFillAttribute; si.dwFlags = m_pObject->m_dwStartFlags; si.wShowWindow = (WORD)m_pObject->m_dwShowWindow; #ifdef HHANCE_DEBUG_CODE DEBUGTRACE((LOG_ESS, "Calling Create process\n")); #endif BOOL bRes = CreateProcessW(szApplicationName, pCommandLine, NULL, NULL, FALSE, m_pObject->m_dwCreationFlags, NULL, szWorkingDirectory, &si, &pi); if (!bRes) { DWORD dwErr = GetLastError(); m_pObject->m_pErrorObj->ReportError(L"CreateProcess", szApplicationName ? szApplicationName : pCommandLine, NULL, dwErr, true); ERRORTRACE((LOG_ESS, "CreateProcess failed, 0x%08X\n", dwErr)); } #ifdef HHANCE_DEBUG_CODE else DEBUGTRACE((LOG_ESS, "Create Process succeeded\n")); #endif // get current time for shutdown info GetSystemTimeAsFileTime(&now); if (!bRes) hr = WBEM_E_FAILED; return hr; } HRESULT STDMETHODCALLTYPE CCommandLineSink::XSink::IndicateToConsumer( IWbemClassObject* pLogicalConsumer, long lNumObjects, IWbemClassObject** apObjects) { HRESULT hr = S_OK; 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 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)) { if (hr == WBEM_E_ACCESS_DENIED) ERRORTRACE((LOG_ESS, "Command line event consumer may only be used by an administrator\n")); return hr; } } else return WBEM_E_OUT_OF_MEMORY; for(int i = 0; i < lNumObjects; i++) { BSTR strCommandLine = m_pObject->m_CommandLine.Apply(apObjects[i]); if(strCommandLine == NULL) { ERRORTRACE((LOG_ESS, "Invalid command line!\n")); return WBEM_E_INVALID_PARAMETER; } WString wsCommandLine = strCommandLine; SysFreeString(strCommandLine); BSTR bstrTitle = m_pObject->m_title.Apply(apObjects[i]); WString wsTitle = bstrTitle; if (bstrTitle) SysFreeString(bstrTitle); FILETIME now; PROCESS_INFORMATION pi; WCHAR* pCommandLine = ((wsCommandLine.Length() == 0) ? NULL : (wchar_t *)wsCommandLine);; WCHAR* pTitle = ((wsTitle.Length() == 0) ? NULL : (wchar_t *)wsTitle); hr = CreateProcessNT(pCommandLine, pTitle, pi, now); if (FAILED(hr)) { ERRORTRACE((LOG_ESS, "Failed to CreateProcess %S. Error 0x%X\n", (LPCWSTR)wsCommandLine, hr)); return hr; } else { if (m_pObject->m_dwKillTimeout) { WAYCOOL_FILETIME then(now); then.AddSeconds(m_pObject->m_dwKillTimeout); hr = g_procKillerTimer.ScheduleAssassination(pi.hProcess, (FILETIME)then); if (FAILED(hr)) DEBUGTRACE((LOG_ESS, "Could not schedule process termination\n")); } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } } return hr; } void* CCommandLineSink::GetInterface(REFIID riid) { if(riid == IID_IWbemUnboundObjectSink) return &m_XSink; else return NULL; }