|
|
// --------------------------------------------------------------------------
// Module Name: BadApplicationManager.cpp
//
// Copyright (c) 2000, Microsoft Corporation
//
// Classes to manage bad applications in the fast user switching environment.
//
// History: 2000-08-25 vtan created
// --------------------------------------------------------------------------
#ifdef _X86_
#include "StandardHeader.h"
#include "BadApplicationManager.h"
#include <wtsapi32.h>
#include <winsta.h>
#include "GracefulTerminateApplication.h"
#include "RestoreApplication.h"
#include "SingleThreadedExecution.h"
#include "StatusCode.h"
#include "TokenInformation.h"
// --------------------------------------------------------------------------
// CBadApplicationManager::INDEX_EVENT
// CBadApplicationManager::INDEX_HANDLES
// CBadApplicationManager::INDEX_RESERVED
// CBadApplicationManager::s_szDefaultDesktop
//
// Purpose: Constant indicies into a HANDLE array passed to
// user32!MsgWaitForMultipleObjects. The first handle is always
// the synchronization event. Subsequent HANDLEs are built into
// a static ARRAY passed with the dynamic amount.
//
// History: 2000-08-25 vtan created
// --------------------------------------------------------------------------
const int CBadApplicationManager::INDEX_EVENT = 0; const int CBadApplicationManager::INDEX_HANDLES = INDEX_EVENT + 1; const int CBadApplicationManager::INDEX_RESERVED = 2; const WCHAR CBadApplicationManager::s_szDefaultDesktop[] = L"WinSta0\\Default";
// --------------------------------------------------------------------------
// CBadApplicationManager::CBadApplicationManager
//
// Arguments: <none>
//
// Returns: <none>
//
// Purpose: Constructor for CBadApplicationManager. This creates a thread
// that watches HANDLEs in the bad application list. The watcher
// knows when the offending process dies. It also creates a
// synchronization event that is signalled when the array of
// bad applications changes (is incremented). The thread
// maintains removal cases.
//
// History: 2000-08-25 vtan created
// --------------------------------------------------------------------------
CBadApplicationManager::CBadApplicationManager (HINSTANCE hInstance) : CThread(), _hInstance(hInstance), _hModule(NULL), _atom(NULL), _hwnd(NULL), _fTerminateWatcherThread(false), _fRegisteredNotification(false), _dwSessionIDLastConnect(static_cast<DWORD>(-1)), _hTokenLastUser(NULL), _hEvent(NULL), _badApplications(sizeof(BAD_APPLICATION_INFO)), _restoreApplications()
{ Resume(); }
// --------------------------------------------------------------------------
// CBadApplicationManager::~CBadApplicationManager
//
// Arguments: <none>
//
// Returns: <none>
//
// Purpose: Destructor for CBadApplicationManager. Releases any resources
// used.
//
// History: 2000-08-25 vtan created
// --------------------------------------------------------------------------
CBadApplicationManager::~CBadApplicationManager (void)
{
// In case the token hasn't been released yet - release it.
ReleaseHandle(_hTokenLastUser); Cleanup(); }
// --------------------------------------------------------------------------
// CBadApplicationManager::Terminate
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Forces the watcher thread to terminate. Acquire the lock. Walk
// the list of entries and release the HANDLE on the process
// objects so they don't leak. Set the bool to terminate the
// thread. Set the event to wake the thread up. Release the lock.
//
// History: 2000-08-25 vtan created
// --------------------------------------------------------------------------
NTSTATUS CBadApplicationManager::Terminate (void)
{ int i; CSingleThreadedExecution listLock(_lock);
for (i = _badApplications.GetCount() - 1; i >= 0; --i) { BAD_APPLICATION_INFO badApplicationInfo;
if (NT_SUCCESS(_badApplications.Get(&badApplicationInfo, i))) { TBOOL(CloseHandle(badApplicationInfo.hProcess)); } _badApplications.Remove(i); } _fTerminateWatcherThread = true; return(_hEvent.Set()); }
// --------------------------------------------------------------------------
// CBadApplicationManager::QueryRunning
//
// Arguments: badApplication = Bad application identifier to query.
// dwSessionID = Session ID of the request.
//
// Returns: bool
//
// Purpose: Queries the current running known bad applications list
// looking for a match. Again because this typically runs on a
// different thread to the watcher thread access to the list is
// protected by a critical section.
//
// History: 2000-08-25 vtan created
// --------------------------------------------------------------------------
bool CBadApplicationManager::QueryRunning (const CBadApplication& badApplication, DWORD dwSessionID)
{ bool fResult; NTSTATUS status; int i; CSingleThreadedExecution listLock(_lock);
status = STATUS_SUCCESS; fResult = false;
// Loop looking for a match. This uses the overloaded operator ==.
for (i = _badApplications.GetCount() - 1; !fResult && (i >= 0); --i) { BAD_APPLICATION_INFO badApplicationInfo;
status = _badApplications.Get(&badApplicationInfo, i); if (NT_SUCCESS(status)) {
// Make sure the client is not in the same session as the running
// bad application. This API exists to prevent cross session instances.
// It's assumed that applications have their own mechanisms for multiple
// instances in the same session (or object name space).
fResult = ((badApplicationInfo.dwSessionID != dwSessionID) && (badApplicationInfo.badApplication == badApplication)); } } TSTATUS(status); return(fResult); }
// --------------------------------------------------------------------------
// CBadApplicationManager::RegisterRunning
//
// Arguments: badApplication = Bad application identifier to add.
// hProcess = HANDLE to the process.
//
// Returns: NTSTATUS
//
// Purpose: Adds the given bad application to the known running list. The
// process object is added as well so that when the process
// terminates it can be cleaned up out of the list.
//
// Access to the bad application list is serialized with a
// critical section. This is important because the thread
// watching for termination always run on a different thread to
// the thread on which this function executes. Because they both
// access the same member variables this must be protected with
// a critical section.
//
// History: 2000-08-25 vtan created
// --------------------------------------------------------------------------
NTSTATUS CBadApplicationManager::RegisterRunning (const CBadApplication& badApplication, HANDLE hProcess, BAM_TYPE bamType)
{ NTSTATUS status; CSingleThreadedExecution listLock(_lock);
ASSERTMSG((bamType > BAM_TYPE_MINIMUM) && (bamType < BAM_TYPE_MAXIMUM), "Invalid BAM_TYPE value passed to CBadApplicationManager::AddRunning");
// Have we reached the maximum number of wait object allowed? If not
// then proceed to add this. Otherwise reject the call. This is a
// hard coded limit in the kernel so we abide by it.
if (_badApplications.GetCount() < (MAXIMUM_WAIT_OBJECTS - INDEX_RESERVED)) { BOOL fResult; BAD_APPLICATION_INFO badApplicationInfo;
// Duplicate the HANDLE with SYNCHRONIZE access. That's
// all we need to call the wait function.
fResult = DuplicateHandle(GetCurrentProcess(), hProcess, GetCurrentProcess(), &badApplicationInfo.hProcess, SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, 0); if (fResult != FALSE) { PROCESS_SESSION_INFORMATION processSessionInformation; ULONG ulReturnLength;
// Add the information to the list.
badApplicationInfo.bamType = bamType; badApplicationInfo.badApplication = badApplication; status = NtQueryInformationProcess(badApplicationInfo.hProcess, ProcessSessionInformation, &processSessionInformation, sizeof(processSessionInformation), &ulReturnLength); if (NT_SUCCESS(status)) { badApplicationInfo.dwSessionID = processSessionInformation.SessionId; status = _badApplications.Add(&badApplicationInfo); if (NT_SUCCESS(status)) { status = _hEvent.Set(); } } } else { status = CStatusCode::StatusCodeOfLastError(); } } else { status = STATUS_UNSUCCESSFUL; } return(status); }
// --------------------------------------------------------------------------
// CBadApplicationManager::QueryInformation
//
// Arguments: badApplication = Bad application identifier to query.
// hProcess = Handle to running process.
//
// Returns: NTSTATUS
//
// Purpose: Finds the given application in the running bad application
// list and returns a duplicated handle to the caller.
//
// History: 2000-08-25 vtan created
// --------------------------------------------------------------------------
NTSTATUS CBadApplicationManager::QueryInformation (const CBadApplication& badApplication, HANDLE& hProcess)
{ NTSTATUS status; bool fResult; int i; CSingleThreadedExecution listLock(_lock);
// Assume failure
hProcess = NULL; status = STATUS_OBJECT_NAME_NOT_FOUND;
fResult = false;
// Loop looking for a match. This uses the overloaded operator ==.
for (i = _badApplications.GetCount() - 1; !fResult && (i >= 0); --i) { BAD_APPLICATION_INFO badApplicationInfo;
if (NT_SUCCESS(_badApplications.Get(&badApplicationInfo, i))) {
// Make sure the client is not in the same session as the running
// bad application. This API exists to prevent cross session instances.
// It's assumed that applications have their own mechanisms for multiple
// instances in the same session (or object name space).
fResult = (badApplicationInfo.badApplication == badApplication); if (fResult) { if (DuplicateHandle(GetCurrentProcess(), badApplicationInfo.hProcess, GetCurrentProcess(), &hProcess, 0, FALSE, DUPLICATE_SAME_ACCESS) != FALSE) { status = STATUS_SUCCESS; } else { status = CStatusCode::StatusCodeOfLastError(); } } } }
return(status); }
// --------------------------------------------------------------------------
// CBadApplicationManager::RequestSwitchUser
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Execute terminate of BAM_TYPE_SWITCH_USER. These appications
// are really poorly behaved. A good example is a DVD player
// which bypasses GDI and draws directly into the VGA stream.
//
// Try to kill these and reject the request if it fails.
//
// History: 2000-11-02 vtan created
// --------------------------------------------------------------------------
NTSTATUS CBadApplicationManager::RequestSwitchUser (void)
{ NTSTATUS status; int i;
// Walk the _badApplications list.
status = STATUS_SUCCESS; _lock.Acquire(); i = _badApplications.GetCount() - 1; while (NT_SUCCESS(status) && (i >= 0)) { BAD_APPLICATION_INFO badApplicationInfo;
if (NT_SUCCESS(_badApplications.Get(&badApplicationInfo, i))) {
// Look for BAM_TYPE_SWITCH_USER processes. It doesn't matter
// what session ID is tagged. This process is getting terminated.
if (badApplicationInfo.bamType == BAM_TYPE_SWITCH_USER) {
// In any case release the lock, kill the process
// remove it from the watch list. Then reset the
// index back to the end of the list. Make sure to
// account for the "--i;" instruction below by not
// decrementing by 1.
_lock.Release(); status = PerformTermination(badApplicationInfo.hProcess, false); _lock.Acquire(); i = _badApplications.GetCount(); } } --i; } _lock.Release(); return(status); }
// --------------------------------------------------------------------------
// CBadApplicationManager::PerformTermination
//
// Arguments: hProcess = Handle to running process.
//
// Returns: NTSTATUS
//
// Purpose: Terminates the given process. This is a common routine used
// by both the internal wait thread of this class as well as
// externally by bad application server itself.
//
// History: 2000-10-23 vtan created
// --------------------------------------------------------------------------
NTSTATUS CBadApplicationManager::PerformTermination (HANDLE hProcess, bool fAllowForceTerminate)
{ NTSTATUS status;
status = TerminateGracefully(hProcess); if (!NT_SUCCESS(status) && fAllowForceTerminate) { status = TerminateForcibly(hProcess); } return(status); }
// --------------------------------------------------------------------------
// CBadApplicationManager::Entry
//
// Arguments: <none>
//
// Returns: DWORD
//
// Purpose: Watcher thread for process objects. This thread builds the
// array of proces handles to wait on as well as including the
// synchronization event that gets signaled by the Add member
// function. When that event is signaled the wait is re-executed
// with the new array of objects to wait on.
//
// When a process object is signaled it is cleared out of the
// known list to allow further creates to succeed.
//
// Acquisition of the critical section is carefully placed in
// this function so that the critical section is not held when
// the wait call is made.
//
// Added to this is a window and a message pump to enable
// listening for session notifications from terminal server.
//
// History: 2000-08-25 vtan created
// 2000-10-23 vtan added HWND message pump mechanism
// --------------------------------------------------------------------------
DWORD CBadApplicationManager::Entry (void)
{ WNDCLASSEX wndClassEx;
// Register this window class.
ZeroMemory(&wndClassEx, sizeof(wndClassEx)); wndClassEx.cbSize = sizeof(WNDCLASSEX); wndClassEx.lpfnWndProc = NotificationWindowProc; wndClassEx.hInstance = _hInstance; wndClassEx.lpszClassName = TEXT("BadApplicationNotificationWindowClass"); _atom = RegisterClassEx(&wndClassEx);
// Create the notification window
_hwnd = CreateWindow(MAKEINTRESOURCE(_atom), TEXT("BadApplicationNotificationWindow"), WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL, _hInstance, this);
if (_hwnd != NULL) { _fRegisteredNotification = (WinStationRegisterConsoleNotification(SERVERNAME_CURRENT, _hwnd, NOTIFY_FOR_ALL_SESSIONS) != FALSE); if (!_fRegisteredNotification) { _hModule = LoadLibrary(TEXT("shsvcs.dll")); if (_hModule != NULL) { DWORD dwThreadID; HANDLE hThread;
// If the register fails then create a thread to wait on the event
// and then register onces it's available. If the thread cannot be
// created it's no biggy. The notification mechanism fails and the
// welcome screen isn't updated.
AddRef(); hThread = CreateThread(NULL, 0, RegisterThreadProc, this, 0, &dwThreadID); if (hThread != NULL) { TBOOL(CloseHandle(hThread)); } else { Release(); TBOOL(FreeLibrary(_hModule)); _hModule = NULL; } } } }
// Acquire the lock. This is necessary because to fill the array of
// handles to wait on requires access to the internal list.
_lock.Acquire(); do { DWORD dwWaitResult; int i, iLimit; BAD_APPLICATION_INFO badApplicationInfo; HANDLE hArray[MAXIMUM_WAIT_OBJECTS];
ZeroMemory(&hArray, sizeof(hArray)); hArray[INDEX_EVENT] = _hEvent; iLimit = _badApplications.GetCount(); for (i = 0; i < iLimit; ++i) { if (NT_SUCCESS(_badApplications.Get(&badApplicationInfo, i))) { hArray[INDEX_HANDLES + i] = badApplicationInfo.hProcess; } }
// Release the lock before we enter the wait state.
// Wait on ANY of the objects to be signaled.
_lock.Release(); dwWaitResult = MsgWaitForMultipleObjects(INDEX_HANDLES + iLimit, hArray, FALSE, INFINITE, QS_ALLINPUT); ASSERTMSG(dwWaitResult != WAIT_FAILED, "WaitForMultipleObjects failed in CBadApplicationManager::Entry");
// We were woken up by an object being signaled. Is this the
// synchronization object?
dwWaitResult -= WAIT_OBJECT_0; if (dwWaitResult == INDEX_EVENT) {
// Yes. Acquire the lock. Reset the synchronization event. It's
// important to acquire the lock before resetting the event because
// the Add function could have the lock and be adding to the list.
// Once the Add function releases the lock it cannot signal the event.
// Otherwise we could reset the event during the Add function adding
// a new object and this would be missed.
_lock.Acquire(); TSTATUS(_hEvent.Reset()); }
// No. Is this a message that requires dispatching as part of the
// message pump?
else if (dwWaitResult == WAIT_OBJECT_0 + INDEX_HANDLES + static_cast<DWORD>(iLimit)) {
// Yes. Remove the message from the message queue and dispatch it.
MSG msg;
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE) { (BOOL)TranslateMessage(&msg); (LRESULT)DispatchMessage(&msg); } _lock.Acquire(); } else {
// No. One of the bad applications we are watching has terminated
// and its proces object is now signaled. Go to the correct index
// in the array. Acquire the lock. Close the HANDLE. It's not needed
// anymore. Then remove the entry from the list.
dwWaitResult -= INDEX_HANDLES; _lock.Acquire(); if (NT_SUCCESS(_badApplications.Get(&badApplicationInfo, dwWaitResult))) { TBOOL(CloseHandle(badApplicationInfo.hProcess)); } TSTATUS(_badApplications.Remove(dwWaitResult)); }
// At this point we still hold the lock. This is important because the top
// of the loop expects the lock to be held to build the HANDLE array.
} while (!_fTerminateWatcherThread);
// Clean up stuff that happened on this thread.
Cleanup();
// If we here then the thread is being terminated for some reason.
// Release the lock. It doesn't matter what happens now anyway.
_lock.Release(); return(0); }
// --------------------------------------------------------------------------
// CBadApplicationManager::TerminateForcibly
//
// Arguments: hProcess = Process to terminate.
//
// Returns: NTSTATUS
//
// Purpose: Inject a user mode thread into the process which calls
// kernel32!ExitProcess. If the thread injection fails then fall
// back to kernel32!TerminatProcess to force in.
//
// History: 2000-10-27 vtan created
// --------------------------------------------------------------------------
NTSTATUS CBadApplicationManager::TerminateForcibly (HANDLE hProcess)
{ NTSTATUS status; HANDLE hProcessTerminate;
// Duplicate the process handle and request all the access required
// to create a remote thread in the process.
if (DuplicateHandle(GetCurrentProcess(), hProcess, GetCurrentProcess(), &hProcessTerminate, SYNCHRONIZE | PROCESS_TERMINATE | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, 0) != FALSE) { DWORD dwWaitResult; HANDLE hThread, hWaitArray[2];
// Go and create the remote thread that immediately turns
// around and calls kernel32!ExitProcess. This allows
// a clean process shutdown to occur. If this times out
// then kill the process with terminate process.
status = RtlCreateUserThread(hProcessTerminate, NULL, FALSE, 0, 0, 0, reinterpret_cast<PUSER_THREAD_START_ROUTINE>(ExitProcess), NULL, &hThread, NULL); if (NT_SUCCESS(status)) {
hWaitArray[0] = hThread; hWaitArray[1] = hProcessTerminate; dwWaitResult = WaitForMultipleObjects(ARRAYSIZE(hWaitArray), hWaitArray, TRUE, 5000); TBOOL(CloseHandle(hThread)); if (dwWaitResult != WAIT_TIMEOUT) { status = STATUS_SUCCESS; } else { status = STATUS_TIMEOUT; } } if (status != STATUS_SUCCESS) { if (TerminateProcess(hProcessTerminate, 0) != FALSE) { status = STATUS_SUCCESS; } else { status = CStatusCode::StatusCodeOfLastError(); } } TBOOL(CloseHandle(hProcessTerminate)); } else { status = CStatusCode::StatusCodeOfLastError(); } return(status); }
// --------------------------------------------------------------------------
// CBadApplicationManager::TerminateGracefully
//
// Arguments: hProcess = Process to terminate.
//
// Returns: NTSTATUS
//
// Purpose: Creates a rundll32 process on the session of the target
// process in WinSta0\Default which will re-enter this dll and
// call the "terminate" functionality. This allows the process to
// walk the window list corresponding to that session and send
// those windows close messages and wait for graceful
// termination.
//
// History: 2000-10-24 vtan created
// --------------------------------------------------------------------------
NTSTATUS CBadApplicationManager::TerminateGracefully (HANDLE hProcess)
{ NTSTATUS status; ULONG ulReturnLength; PROCESS_BASIC_INFORMATION processBasicInformation;
status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &processBasicInformation, sizeof(processBasicInformation), &ulReturnLength); if (NT_SUCCESS(status)) { HANDLE hToken;
if (OpenProcessToken(hProcess, TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY, &hToken) != FALSE) { STARTUPINFOW startupInfo; PROCESS_INFORMATION processInformation; WCHAR szCommandLine[MAX_PATH];
ZeroMemory(&startupInfo, sizeof(startupInfo)); ZeroMemory(&processInformation, sizeof(processInformation)); startupInfo.cb = sizeof(startupInfo); startupInfo.lpDesktop = const_cast<WCHAR*>(s_szDefaultDesktop); wsprintfW(szCommandLine, L"rundll32 shsvcs.dll,FUSCompatibilityEntry terminate %d", static_cast<DWORD>(processBasicInformation.UniqueProcessId)); if (CreateProcessAsUserW(hToken, NULL, szCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInformation) != FALSE) { DWORD dwWaitResult; HANDLE hArray[2];
// Assume that this whole thing failed.
status = STATUS_UNSUCCESSFUL; TBOOL(CloseHandle(processInformation.hThread));
// Wait on both process objects. If the process to be terminated
// is signaled then the rundll32 stub did its job. If the rundll32
// stub is signaled then find out what its exit code is and either
// continue waiting on the process to be terminated or return back
// a code to the caller indicating success or failure. Failure
// forces the process to be terminated abruptly.
hArray[0] = hProcess; hArray[1] = processInformation.hProcess; dwWaitResult = WaitForMultipleObjects(ARRAYSIZE(hArray), hArray, FALSE, 10000);
// If the process to be terminated is signaled then we're done.
if (dwWaitResult == WAIT_OBJECT_0) { status = STATUS_SUCCESS; }
// If the rundll32 stub is signaled then find out what it found.
else if (dwWaitResult == WAIT_OBJECT_0 + 1) { DWORD dwExitCode;
dwExitCode = STILL_ACTIVE; if (GetExitCodeProcess(processInformation.hProcess, &dwExitCode) != FALSE) { ASSERTMSG((dwExitCode == CGracefulTerminateApplication::NO_WINDOWS_FOUND) || (dwExitCode == CGracefulTerminateApplication::WAIT_WINDOWS_FOUND), "Unexpected process exit code in CBadApplicationManager::TerminateGracefully");
// If the rundll32 stub says it found some windows then
// wait for the process to terminate itself.
if (dwExitCode == CGracefulTerminateApplication::WAIT_WINDOWS_FOUND) {
// If the process terminates within the timeout period
// then we're done.
if (WaitForSingleObject(hProcess, 10000) == WAIT_OBJECT_0) { status = STATUS_SUCCESS; } } } } TBOOL(CloseHandle(processInformation.hProcess)); } else { status = CStatusCode::StatusCodeOfLastError(); } TBOOL(CloseHandle(hToken)); } else { status = CStatusCode::StatusCodeOfLastError(); } } return(status); }
// --------------------------------------------------------------------------
// CBadApplicationManager::Cleanup
//
// Arguments: <none>
//
// Returns: <none>
//
// Purpose: Releases used resources in the class. Used by both the
// constructor and the thread - whoever wins.
//
// History: 2000-12-12 vtan created
// --------------------------------------------------------------------------
void CBadApplicationManager::Cleanup (void)
{ if (_fRegisteredNotification) { (BOOL)WinStationUnRegisterConsoleNotification(SERVERNAME_CURRENT, _hwnd); _fRegisteredNotification = false; } if (_hwnd != NULL) { TBOOL(DestroyWindow(_hwnd)); _hwnd = NULL; } if (_atom != 0) { TBOOL(UnregisterClass(MAKEINTRESOURCE(_atom), _hInstance)); _atom = 0; } }
// --------------------------------------------------------------------------
// CBadApplicationManager::Handle_Logon
//
// Arguments: <none>
//
// Returns: <none>
//
// Purpose: Nothing at present.
//
// History: 2000-10-24 vtan created
// --------------------------------------------------------------------------
void CBadApplicationManager::Handle_Logon (void)
{ }
// --------------------------------------------------------------------------
// CBadApplicationManager::Handle_Logoff
//
// Arguments: dwSessionID = Session ID that is logging off.
//
// Returns: <none>
//
// Purpose: Remove any restore processes we have in the list. The user
// is logging off so they shouldn't come back. Releases the last
// user to actively connect to the machine.
//
// History: 2000-10-24 vtan created
// --------------------------------------------------------------------------
void CBadApplicationManager::Handle_Logoff (DWORD dwSessionID)
{ int i; CSingleThreadedExecution listLock(_lock);
for (i = _restoreApplications.GetCount() - 1; i >= 0; --i) { CRestoreApplication *pRestoreApplication;
pRestoreApplication = static_cast<CRestoreApplication*>(_restoreApplications.Get(i)); if ((pRestoreApplication != NULL) && pRestoreApplication->IsEqualSessionID(dwSessionID)) { TSTATUS(_restoreApplications.Remove(i)); } } ReleaseHandle(_hTokenLastUser); }
// --------------------------------------------------------------------------
// CBadApplicationManager::Handle_Connect
//
// Arguments: dwSessionID = Session ID connecting.
// hToken = Handle to token of user connecting.
//
// Returns: <none>
//
// Purpose: Handles BAM3. This is the save for restoration all processes
// that use resources that aren't easily shared and restore all
// processes that were saved which aren't easily shared.
//
// It's optimized for not closing the processes of the same user
// should that user re-connect. This allows the screen saver to
// kick in and return to welcome without killing the user's
// processes unnecessarily.
//
// Also handles BAM4.
//
// History: 2000-10-24 vtan created
// --------------------------------------------------------------------------
void CBadApplicationManager::Handle_Connect (DWORD dwSessionID, HANDLE hToken)
{ if ((_hTokenLastUser != NULL) && (hToken != NULL)) { PSID pSIDLastUser, pSIDCurrentUser; CTokenInformation tokenLastUser(_hTokenLastUser); CTokenInformation tokenCurrentUser(hToken);
pSIDLastUser = tokenLastUser.GetUserSID(); pSIDCurrentUser = tokenCurrentUser.GetUserSID(); if ((pSIDLastUser != NULL) && (pSIDCurrentUser != NULL) && !EqualSid(pSIDLastUser, pSIDCurrentUser)) { int i; DWORD dwSessionIDMatch; ULONG ulReturnLength; CRestoreApplication *pRestoreApplication;
if (NT_SUCCESS(NtQueryInformationToken(_hTokenLastUser, TokenSessionId, &dwSessionIDMatch, sizeof(dwSessionIDMatch), &ulReturnLength))) {
// Walk the _badApplications list.
_lock.Acquire(); i = _badApplications.GetCount() - 1; while (i >= 0) { BAD_APPLICATION_INFO badApplicationInfo;
if (NT_SUCCESS(_badApplications.Get(&badApplicationInfo, i))) { bool fTerminateProcess;
fTerminateProcess = false;
// Look for BAM_TYPE_SWITCH_TO_NEW_USER_WITH_RESTORE processes
// which have token session IDs that match the _hTokenLastUser
// session ID. These processes must be terminated and added to
// a list to be restarted on reconnection.
if ((badApplicationInfo.bamType == BAM_TYPE_SWITCH_TO_NEW_USER_WITH_RESTORE) && (badApplicationInfo.dwSessionID == dwSessionIDMatch)) { pRestoreApplication = new CRestoreApplication; if (pRestoreApplication != NULL) { if (NT_SUCCESS(pRestoreApplication->GetInformation(badApplicationInfo.hProcess))) { TSTATUS(_restoreApplications.Add(pRestoreApplication)); fTerminateProcess = true; } pRestoreApplication->Release(); } }
// Look for BAM_TYPE_SWITCH_TO_NEW_USER (even though this is
// a connect/reconnect). Always kill these processes.
if (badApplicationInfo.bamType == BAM_TYPE_SWITCH_TO_NEW_USER) { fTerminateProcess = true; } if (fTerminateProcess) {
// In any case release the lock, kill the process
// remove it from the watch list. Then reset the
// index back to the end of the list. Make sure to
// account for the "--i;" instruction below by not
// decrementing by 1.
_lock.Release(); TSTATUS(PerformTermination(badApplicationInfo.hProcess, true)); _lock.Acquire(); TBOOL(CloseHandle(badApplicationInfo.hProcess)); TSTATUS(_badApplications.Remove(i)); i = _badApplications.GetCount(); } } --i; } _lock.Release(); }
// Now walk the restore list looking for matches against the
// connecting session ID. Restore these processes.
_lock.Acquire(); i = _restoreApplications.GetCount() - 1; while (i >= 0) { pRestoreApplication = static_cast<CRestoreApplication*>(_restoreApplications.Get(i)); if ((pRestoreApplication != NULL) && pRestoreApplication->IsEqualSessionID(dwSessionID)) { HANDLE hProcess;
_lock.Release(); if (NT_SUCCESS(pRestoreApplication->Restore(&hProcess))) { CBadApplication badApplication(pRestoreApplication->GetCommandLine());
TBOOL(CloseHandle(hProcess)); } _lock.Acquire(); TSTATUS(_restoreApplications.Remove(i)); i = _restoreApplications.GetCount(); } --i; } _lock.Release(); } } if (hToken != NULL) { _dwSessionIDLastConnect = static_cast<DWORD>(-1); } else { _dwSessionIDLastConnect = dwSessionID; } }
// --------------------------------------------------------------------------
// CBadApplicationManager::Handle_Disconnect
//
// Arguments: dwSessionID = Session ID that is disconnecting.
// hToken = Token of the user disconnecting.
//
// Returns: <none>
//
// Purpose: If the session isn't the same as the last connected session
// then release the last user token and save the current one.
//
// History: 2000-10-24 vtan created
// --------------------------------------------------------------------------
void CBadApplicationManager::Handle_Disconnect (DWORD dwSessionID, HANDLE hToken)
{ if (_dwSessionIDLastConnect != dwSessionID) { ReleaseHandle(_hTokenLastUser); if (hToken != NULL) { TBOOL(DuplicateHandle(GetCurrentProcess(), hToken, GetCurrentProcess(), &_hTokenLastUser, 0, FALSE, DUPLICATE_SAME_ACCESS)); } } }
// --------------------------------------------------------------------------
// CBadApplicationManager::Handle_WM_WTSSESSION_CHANGE
//
// Arguments: wParam = Type of session change.
// lParam = Pointer to WTSSESSION_NOTIFICATION struct.
//
// Returns: LRESULT
//
// Purpose: Handles WM_WTSSESSION_CHANGE messages.
//
// History: 2000-10-23 vtan created
// --------------------------------------------------------------------------
LRESULT CBadApplicationManager::Handle_WM_WTSSESSION_CHANGE (WPARAM wParam, LPARAM lParam)
{ ULONG ulReturnLength; WINSTATIONUSERTOKEN winStationUserToken;
winStationUserToken.ProcessId = reinterpret_cast<HANDLE>(GetCurrentProcessId()); winStationUserToken.ThreadId = reinterpret_cast<HANDLE>(GetCurrentThreadId()); winStationUserToken.UserToken = NULL; (BOOLEAN)WinStationQueryInformation(SERVERNAME_CURRENT, lParam, WinStationUserToken, &winStationUserToken, sizeof(winStationUserToken), &ulReturnLength); switch (wParam) { case WTS_SESSION_LOGOFF: Handle_Logoff(lParam); break; case WTS_SESSION_LOGON: Handle_Logon(); // Fall thru to connect case.
case WTS_CONSOLE_CONNECT: case WTS_REMOTE_CONNECT: Handle_Connect(lParam, winStationUserToken.UserToken); break; case WTS_CONSOLE_DISCONNECT: case WTS_REMOTE_DISCONNECT: Handle_Disconnect(lParam, winStationUserToken.UserToken); break; default: break; } if (winStationUserToken.UserToken != NULL) { TBOOL(CloseHandle(winStationUserToken.UserToken)); } return(1); }
// --------------------------------------------------------------------------
// CBadApplicationManager::NotificationWindowProc
//
// Arguments: See the platform SDK under WindowProc.
//
// Returns: LRESULT
//
// Purpose: Handles messages for the Notification window.
//
// History: 2000-10-23 vtan created
// --------------------------------------------------------------------------
LRESULT CALLBACK CBadApplicationManager::NotificationWindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{ LRESULT lResult; CBadApplicationManager *pThis;
pThis = reinterpret_cast<CBadApplicationManager*>(GetWindowLongPtr(hwnd, GWLP_USERDATA)); switch (uMsg) { case WM_CREATE: { CREATESTRUCT *pCreateStruct;
pCreateStruct = reinterpret_cast<CREATESTRUCT*>(lParam); (LONG_PTR)SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pCreateStruct->lpCreateParams)); lResult = 0; break; } case WM_WTSSESSION_CHANGE: lResult = pThis->Handle_WM_WTSSESSION_CHANGE(wParam, lParam); break; default: lResult = DefWindowProc(hwnd, uMsg, wParam, lParam); break; } return(lResult); }
// --------------------------------------------------------------------------
// CBadApplicationManager::RegisterThreadProc
//
// Arguments: pParameter = Object pointer.
//
// Returns: DWORD
//
// Purpose: Opens the TermSrvReadyEvent and waits on it. Once ready it
// registers for a notifications.
//
// History: 2000-10-23 vtan created
// --------------------------------------------------------------------------
DWORD WINAPI CBadApplicationManager::RegisterThreadProc (void *pParameter)
{ int iCounter; HANDLE hTermSrvReadyEvent; HMODULE hModule; CBadApplicationManager *pThis;
pThis = reinterpret_cast<CBadApplicationManager*>(pParameter); hModule = pThis->_hModule; ASSERTMSG(hModule != NULL, "NULL HMODULE in CBadApplicationManager::RegisterThreadProc"); iCounter = 0; hTermSrvReadyEvent = OpenEvent(SYNCHRONIZE, FALSE, TEXT("TermSrvReadyEvent")); while ((hTermSrvReadyEvent == NULL) && (iCounter < 60)) { ++iCounter; Sleep(1000); hTermSrvReadyEvent = OpenEvent(SYNCHRONIZE, FALSE, TEXT("TermSrvReadyEvent")); } if (hTermSrvReadyEvent != NULL) { if (WaitForSingleObject(hTermSrvReadyEvent, 60000) == WAIT_OBJECT_0) { pThis->_fRegisteredNotification = (WinStationRegisterConsoleNotification(SERVERNAME_CURRENT, pThis->_hwnd, NOTIFY_FOR_ALL_SESSIONS) != FALSE); } TBOOL(CloseHandle(hTermSrvReadyEvent)); } pThis->Release(); FreeLibraryAndExitThread(hModule, 0); }
#endif /* _X86_ */
|