Source code of Windows XP (NT5)
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.

1242 lines
45 KiB

  1. // --------------------------------------------------------------------------
  2. // Module Name: BadApplicationManager.cpp
  3. //
  4. // Copyright (c) 2000, Microsoft Corporation
  5. //
  6. // Classes to manage bad applications in the fast user switching environment.
  7. //
  8. // History: 2000-08-25 vtan created
  9. // --------------------------------------------------------------------------
  10. #ifdef _X86_
  11. #include "StandardHeader.h"
  12. #include "BadApplicationManager.h"
  13. #include <wtsapi32.h>
  14. #include <winsta.h>
  15. #include "GracefulTerminateApplication.h"
  16. #include "RestoreApplication.h"
  17. #include "SingleThreadedExecution.h"
  18. #include "StatusCode.h"
  19. #include "TokenInformation.h"
  20. // --------------------------------------------------------------------------
  21. // CBadApplicationManager::INDEX_EVENT
  22. // CBadApplicationManager::INDEX_HANDLES
  23. // CBadApplicationManager::INDEX_RESERVED
  24. // CBadApplicationManager::s_szDefaultDesktop
  25. //
  26. // Purpose: Constant indicies into a HANDLE array passed to
  27. // user32!MsgWaitForMultipleObjects. The first handle is always
  28. // the synchronization event. Subsequent HANDLEs are built into
  29. // a static ARRAY passed with the dynamic amount.
  30. //
  31. // History: 2000-08-25 vtan created
  32. // --------------------------------------------------------------------------
  33. const int CBadApplicationManager::INDEX_EVENT = 0;
  34. const int CBadApplicationManager::INDEX_HANDLES = INDEX_EVENT + 1;
  35. const int CBadApplicationManager::INDEX_RESERVED = 2;
  36. const WCHAR CBadApplicationManager::s_szDefaultDesktop[] = L"WinSta0\\Default";
  37. // --------------------------------------------------------------------------
  38. // CBadApplicationManager::CBadApplicationManager
  39. //
  40. // Arguments: <none>
  41. //
  42. // Returns: <none>
  43. //
  44. // Purpose: Constructor for CBadApplicationManager. This creates a thread
  45. // that watches HANDLEs in the bad application list. The watcher
  46. // knows when the offending process dies. It also creates a
  47. // synchronization event that is signalled when the array of
  48. // bad applications changes (is incremented). The thread
  49. // maintains removal cases.
  50. //
  51. // History: 2000-08-25 vtan created
  52. // --------------------------------------------------------------------------
  53. CBadApplicationManager::CBadApplicationManager (HINSTANCE hInstance) :
  54. CThread(),
  55. _hInstance(hInstance),
  56. _hModule(NULL),
  57. _atom(NULL),
  58. _hwnd(NULL),
  59. _fTerminateWatcherThread(false),
  60. _fRegisteredNotification(false),
  61. _dwSessionIDLastConnect(static_cast<DWORD>(-1)),
  62. _hTokenLastUser(NULL),
  63. _hEvent(NULL),
  64. _badApplications(sizeof(BAD_APPLICATION_INFO)),
  65. _restoreApplications()
  66. {
  67. Resume();
  68. }
  69. // --------------------------------------------------------------------------
  70. // CBadApplicationManager::~CBadApplicationManager
  71. //
  72. // Arguments: <none>
  73. //
  74. // Returns: <none>
  75. //
  76. // Purpose: Destructor for CBadApplicationManager. Releases any resources
  77. // used.
  78. //
  79. // History: 2000-08-25 vtan created
  80. // --------------------------------------------------------------------------
  81. CBadApplicationManager::~CBadApplicationManager (void)
  82. {
  83. // In case the token hasn't been released yet - release it.
  84. ReleaseHandle(_hTokenLastUser);
  85. Cleanup();
  86. }
  87. // --------------------------------------------------------------------------
  88. // CBadApplicationManager::Terminate
  89. //
  90. // Arguments: <none>
  91. //
  92. // Returns: NTSTATUS
  93. //
  94. // Purpose: Forces the watcher thread to terminate. Acquire the lock. Walk
  95. // the list of entries and release the HANDLE on the process
  96. // objects so they don't leak. Set the bool to terminate the
  97. // thread. Set the event to wake the thread up. Release the lock.
  98. //
  99. // History: 2000-08-25 vtan created
  100. // --------------------------------------------------------------------------
  101. NTSTATUS CBadApplicationManager::Terminate (void)
  102. {
  103. int i;
  104. CSingleThreadedExecution listLock(_lock);
  105. for (i = _badApplications.GetCount() - 1; i >= 0; --i)
  106. {
  107. BAD_APPLICATION_INFO badApplicationInfo;
  108. if (NT_SUCCESS(_badApplications.Get(&badApplicationInfo, i)))
  109. {
  110. TBOOL(CloseHandle(badApplicationInfo.hProcess));
  111. }
  112. _badApplications.Remove(i);
  113. }
  114. _fTerminateWatcherThread = true;
  115. return(_hEvent.Set());
  116. }
  117. // --------------------------------------------------------------------------
  118. // CBadApplicationManager::QueryRunning
  119. //
  120. // Arguments: badApplication = Bad application identifier to query.
  121. // dwSessionID = Session ID of the request.
  122. //
  123. // Returns: bool
  124. //
  125. // Purpose: Queries the current running known bad applications list
  126. // looking for a match. Again because this typically runs on a
  127. // different thread to the watcher thread access to the list is
  128. // protected by a critical section.
  129. //
  130. // History: 2000-08-25 vtan created
  131. // --------------------------------------------------------------------------
  132. bool CBadApplicationManager::QueryRunning (const CBadApplication& badApplication, DWORD dwSessionID)
  133. {
  134. bool fResult;
  135. NTSTATUS status;
  136. int i;
  137. CSingleThreadedExecution listLock(_lock);
  138. status = STATUS_SUCCESS;
  139. fResult = false;
  140. // Loop looking for a match. This uses the overloaded operator ==.
  141. for (i = _badApplications.GetCount() - 1; !fResult && (i >= 0); --i)
  142. {
  143. BAD_APPLICATION_INFO badApplicationInfo;
  144. status = _badApplications.Get(&badApplicationInfo, i);
  145. if (NT_SUCCESS(status))
  146. {
  147. // Make sure the client is not in the same session as the running
  148. // bad application. This API exists to prevent cross session instances.
  149. // It's assumed that applications have their own mechanisms for multiple
  150. // instances in the same session (or object name space).
  151. fResult = ((badApplicationInfo.dwSessionID != dwSessionID) &&
  152. (badApplicationInfo.badApplication == badApplication));
  153. }
  154. }
  155. TSTATUS(status);
  156. return(fResult);
  157. }
  158. // --------------------------------------------------------------------------
  159. // CBadApplicationManager::RegisterRunning
  160. //
  161. // Arguments: badApplication = Bad application identifier to add.
  162. // hProcess = HANDLE to the process.
  163. //
  164. // Returns: NTSTATUS
  165. //
  166. // Purpose: Adds the given bad application to the known running list. The
  167. // process object is added as well so that when the process
  168. // terminates it can be cleaned up out of the list.
  169. //
  170. // Access to the bad application list is serialized with a
  171. // critical section. This is important because the thread
  172. // watching for termination always run on a different thread to
  173. // the thread on which this function executes. Because they both
  174. // access the same member variables this must be protected with
  175. // a critical section.
  176. //
  177. // History: 2000-08-25 vtan created
  178. // --------------------------------------------------------------------------
  179. NTSTATUS CBadApplicationManager::RegisterRunning (const CBadApplication& badApplication, HANDLE hProcess, BAM_TYPE bamType)
  180. {
  181. NTSTATUS status;
  182. CSingleThreadedExecution listLock(_lock);
  183. ASSERTMSG((bamType > BAM_TYPE_MINIMUM) && (bamType < BAM_TYPE_MAXIMUM), "Invalid BAM_TYPE value passed to CBadApplicationManager::AddRunning");
  184. // Have we reached the maximum number of wait object allowed? If not
  185. // then proceed to add this. Otherwise reject the call. This is a
  186. // hard coded limit in the kernel so we abide by it.
  187. if (_badApplications.GetCount() < (MAXIMUM_WAIT_OBJECTS - INDEX_RESERVED))
  188. {
  189. BOOL fResult;
  190. BAD_APPLICATION_INFO badApplicationInfo;
  191. // Duplicate the HANDLE with SYNCHRONIZE access. That's
  192. // all we need to call the wait function.
  193. fResult = DuplicateHandle(GetCurrentProcess(),
  194. hProcess,
  195. GetCurrentProcess(),
  196. &badApplicationInfo.hProcess,
  197. SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
  198. FALSE,
  199. 0);
  200. if (fResult != FALSE)
  201. {
  202. PROCESS_SESSION_INFORMATION processSessionInformation;
  203. ULONG ulReturnLength;
  204. // Add the information to the list.
  205. badApplicationInfo.bamType = bamType;
  206. badApplicationInfo.badApplication = badApplication;
  207. status = NtQueryInformationProcess(badApplicationInfo.hProcess,
  208. ProcessSessionInformation,
  209. &processSessionInformation,
  210. sizeof(processSessionInformation),
  211. &ulReturnLength);
  212. if (NT_SUCCESS(status))
  213. {
  214. badApplicationInfo.dwSessionID = processSessionInformation.SessionId;
  215. status = _badApplications.Add(&badApplicationInfo);
  216. if (NT_SUCCESS(status))
  217. {
  218. status = _hEvent.Set();
  219. }
  220. }
  221. }
  222. else
  223. {
  224. status = CStatusCode::StatusCodeOfLastError();
  225. }
  226. }
  227. else
  228. {
  229. status = STATUS_UNSUCCESSFUL;
  230. }
  231. return(status);
  232. }
  233. // --------------------------------------------------------------------------
  234. // CBadApplicationManager::QueryInformation
  235. //
  236. // Arguments: badApplication = Bad application identifier to query.
  237. // hProcess = Handle to running process.
  238. //
  239. // Returns: NTSTATUS
  240. //
  241. // Purpose: Finds the given application in the running bad application
  242. // list and returns a duplicated handle to the caller.
  243. //
  244. // History: 2000-08-25 vtan created
  245. // --------------------------------------------------------------------------
  246. NTSTATUS CBadApplicationManager::QueryInformation (const CBadApplication& badApplication, HANDLE& hProcess)
  247. {
  248. NTSTATUS status;
  249. bool fResult;
  250. int i;
  251. CSingleThreadedExecution listLock(_lock);
  252. // Assume failure
  253. hProcess = NULL;
  254. status = STATUS_OBJECT_NAME_NOT_FOUND;
  255. fResult = false;
  256. // Loop looking for a match. This uses the overloaded operator ==.
  257. for (i = _badApplications.GetCount() - 1; !fResult && (i >= 0); --i)
  258. {
  259. BAD_APPLICATION_INFO badApplicationInfo;
  260. if (NT_SUCCESS(_badApplications.Get(&badApplicationInfo, i)))
  261. {
  262. // Make sure the client is not in the same session as the running
  263. // bad application. This API exists to prevent cross session instances.
  264. // It's assumed that applications have their own mechanisms for multiple
  265. // instances in the same session (or object name space).
  266. fResult = (badApplicationInfo.badApplication == badApplication);
  267. if (fResult)
  268. {
  269. if (DuplicateHandle(GetCurrentProcess(),
  270. badApplicationInfo.hProcess,
  271. GetCurrentProcess(),
  272. &hProcess,
  273. 0,
  274. FALSE,
  275. DUPLICATE_SAME_ACCESS) != FALSE)
  276. {
  277. status = STATUS_SUCCESS;
  278. }
  279. else
  280. {
  281. status = CStatusCode::StatusCodeOfLastError();
  282. }
  283. }
  284. }
  285. }
  286. return(status);
  287. }
  288. // --------------------------------------------------------------------------
  289. // CBadApplicationManager::RequestSwitchUser
  290. //
  291. // Arguments: <none>
  292. //
  293. // Returns: NTSTATUS
  294. //
  295. // Purpose: Execute terminate of BAM_TYPE_SWITCH_USER. These appications
  296. // are really poorly behaved. A good example is a DVD player
  297. // which bypasses GDI and draws directly into the VGA stream.
  298. //
  299. // Try to kill these and reject the request if it fails.
  300. //
  301. // History: 2000-11-02 vtan created
  302. // --------------------------------------------------------------------------
  303. NTSTATUS CBadApplicationManager::RequestSwitchUser (void)
  304. {
  305. NTSTATUS status;
  306. int i;
  307. // Walk the _badApplications list.
  308. status = STATUS_SUCCESS;
  309. _lock.Acquire();
  310. i = _badApplications.GetCount() - 1;
  311. while (NT_SUCCESS(status) && (i >= 0))
  312. {
  313. BAD_APPLICATION_INFO badApplicationInfo;
  314. if (NT_SUCCESS(_badApplications.Get(&badApplicationInfo, i)))
  315. {
  316. // Look for BAM_TYPE_SWITCH_USER processes. It doesn't matter
  317. // what session ID is tagged. This process is getting terminated.
  318. if (badApplicationInfo.bamType == BAM_TYPE_SWITCH_USER)
  319. {
  320. // In any case release the lock, kill the process
  321. // remove it from the watch list. Then reset the
  322. // index back to the end of the list. Make sure to
  323. // account for the "--i;" instruction below by not
  324. // decrementing by 1.
  325. _lock.Release();
  326. status = PerformTermination(badApplicationInfo.hProcess, false);
  327. _lock.Acquire();
  328. i = _badApplications.GetCount();
  329. }
  330. }
  331. --i;
  332. }
  333. _lock.Release();
  334. return(status);
  335. }
  336. // --------------------------------------------------------------------------
  337. // CBadApplicationManager::PerformTermination
  338. //
  339. // Arguments: hProcess = Handle to running process.
  340. //
  341. // Returns: NTSTATUS
  342. //
  343. // Purpose: Terminates the given process. This is a common routine used
  344. // by both the internal wait thread of this class as well as
  345. // externally by bad application server itself.
  346. //
  347. // History: 2000-10-23 vtan created
  348. // --------------------------------------------------------------------------
  349. NTSTATUS CBadApplicationManager::PerformTermination (HANDLE hProcess, bool fAllowForceTerminate)
  350. {
  351. NTSTATUS status;
  352. status = TerminateGracefully(hProcess);
  353. if (!NT_SUCCESS(status) && fAllowForceTerminate)
  354. {
  355. status = TerminateForcibly(hProcess);
  356. }
  357. return(status);
  358. }
  359. // --------------------------------------------------------------------------
  360. // CBadApplicationManager::Entry
  361. //
  362. // Arguments: <none>
  363. //
  364. // Returns: DWORD
  365. //
  366. // Purpose: Watcher thread for process objects. This thread builds the
  367. // array of proces handles to wait on as well as including the
  368. // synchronization event that gets signaled by the Add member
  369. // function. When that event is signaled the wait is re-executed
  370. // with the new array of objects to wait on.
  371. //
  372. // When a process object is signaled it is cleared out of the
  373. // known list to allow further creates to succeed.
  374. //
  375. // Acquisition of the critical section is carefully placed in
  376. // this function so that the critical section is not held when
  377. // the wait call is made.
  378. //
  379. // Added to this is a window and a message pump to enable
  380. // listening for session notifications from terminal server.
  381. //
  382. // History: 2000-08-25 vtan created
  383. // 2000-10-23 vtan added HWND message pump mechanism
  384. // --------------------------------------------------------------------------
  385. DWORD CBadApplicationManager::Entry (void)
  386. {
  387. WNDCLASSEX wndClassEx;
  388. // Register this window class.
  389. ZeroMemory(&wndClassEx, sizeof(wndClassEx));
  390. wndClassEx.cbSize = sizeof(WNDCLASSEX);
  391. wndClassEx.lpfnWndProc = NotificationWindowProc;
  392. wndClassEx.hInstance = _hInstance;
  393. wndClassEx.lpszClassName = TEXT("BadApplicationNotificationWindowClass");
  394. _atom = RegisterClassEx(&wndClassEx);
  395. // Create the notification window
  396. _hwnd = CreateWindow(MAKEINTRESOURCE(_atom),
  397. TEXT("BadApplicationNotificationWindow"),
  398. WS_OVERLAPPED,
  399. 0, 0,
  400. 0, 0,
  401. NULL,
  402. NULL,
  403. _hInstance,
  404. this);
  405. if (_hwnd != NULL)
  406. {
  407. _fRegisteredNotification = (WinStationRegisterConsoleNotification(SERVERNAME_CURRENT, _hwnd, NOTIFY_FOR_ALL_SESSIONS) != FALSE);
  408. if (!_fRegisteredNotification)
  409. {
  410. _hModule = LoadLibrary(TEXT("shsvcs.dll"));
  411. if (_hModule != NULL)
  412. {
  413. DWORD dwThreadID;
  414. HANDLE hThread;
  415. // If the register fails then create a thread to wait on the event
  416. // and then register onces it's available. If the thread cannot be
  417. // created it's no biggy. The notification mechanism fails and the
  418. // welcome screen isn't updated.
  419. AddRef();
  420. hThread = CreateThread(NULL,
  421. 0,
  422. RegisterThreadProc,
  423. this,
  424. 0,
  425. &dwThreadID);
  426. if (hThread != NULL)
  427. {
  428. TBOOL(CloseHandle(hThread));
  429. }
  430. else
  431. {
  432. Release();
  433. TBOOL(FreeLibrary(_hModule));
  434. _hModule = NULL;
  435. }
  436. }
  437. }
  438. }
  439. // Acquire the lock. This is necessary because to fill the array of
  440. // handles to wait on requires access to the internal list.
  441. _lock.Acquire();
  442. do
  443. {
  444. DWORD dwWaitResult;
  445. int i, iLimit;
  446. BAD_APPLICATION_INFO badApplicationInfo;
  447. HANDLE hArray[MAXIMUM_WAIT_OBJECTS];
  448. ZeroMemory(&hArray, sizeof(hArray));
  449. hArray[INDEX_EVENT] = _hEvent;
  450. iLimit = _badApplications.GetCount();
  451. for (i = 0; i < iLimit; ++i)
  452. {
  453. if (NT_SUCCESS(_badApplications.Get(&badApplicationInfo, i)))
  454. {
  455. hArray[INDEX_HANDLES + i] = badApplicationInfo.hProcess;
  456. }
  457. }
  458. // Release the lock before we enter the wait state.
  459. // Wait on ANY of the objects to be signaled.
  460. _lock.Release();
  461. dwWaitResult = MsgWaitForMultipleObjects(INDEX_HANDLES + iLimit,
  462. hArray,
  463. FALSE,
  464. INFINITE,
  465. QS_ALLINPUT);
  466. ASSERTMSG(dwWaitResult != WAIT_FAILED, "WaitForMultipleObjects failed in CBadApplicationManager::Entry");
  467. // We were woken up by an object being signaled. Is this the
  468. // synchronization object?
  469. dwWaitResult -= WAIT_OBJECT_0;
  470. if (dwWaitResult == INDEX_EVENT)
  471. {
  472. // Yes. Acquire the lock. Reset the synchronization event. It's
  473. // important to acquire the lock before resetting the event because
  474. // the Add function could have the lock and be adding to the list.
  475. // Once the Add function releases the lock it cannot signal the event.
  476. // Otherwise we could reset the event during the Add function adding
  477. // a new object and this would be missed.
  478. _lock.Acquire();
  479. TSTATUS(_hEvent.Reset());
  480. }
  481. // No. Is this a message that requires dispatching as part of the
  482. // message pump?
  483. else if (dwWaitResult == WAIT_OBJECT_0 + INDEX_HANDLES + static_cast<DWORD>(iLimit))
  484. {
  485. // Yes. Remove the message from the message queue and dispatch it.
  486. MSG msg;
  487. if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE)
  488. {
  489. (BOOL)TranslateMessage(&msg);
  490. (LRESULT)DispatchMessage(&msg);
  491. }
  492. _lock.Acquire();
  493. }
  494. else
  495. {
  496. // No. One of the bad applications we are watching has terminated
  497. // and its proces object is now signaled. Go to the correct index
  498. // in the array. Acquire the lock. Close the HANDLE. It's not needed
  499. // anymore. Then remove the entry from the list.
  500. dwWaitResult -= INDEX_HANDLES;
  501. _lock.Acquire();
  502. if (NT_SUCCESS(_badApplications.Get(&badApplicationInfo, dwWaitResult)))
  503. {
  504. TBOOL(CloseHandle(badApplicationInfo.hProcess));
  505. }
  506. TSTATUS(_badApplications.Remove(dwWaitResult));
  507. }
  508. // At this point we still hold the lock. This is important because the top
  509. // of the loop expects the lock to be held to build the HANDLE array.
  510. } while (!_fTerminateWatcherThread);
  511. // Clean up stuff that happened on this thread.
  512. Cleanup();
  513. // If we here then the thread is being terminated for some reason.
  514. // Release the lock. It doesn't matter what happens now anyway.
  515. _lock.Release();
  516. return(0);
  517. }
  518. // --------------------------------------------------------------------------
  519. // CBadApplicationManager::TerminateForcibly
  520. //
  521. // Arguments: hProcess = Process to terminate.
  522. //
  523. // Returns: NTSTATUS
  524. //
  525. // Purpose: Inject a user mode thread into the process which calls
  526. // kernel32!ExitProcess. If the thread injection fails then fall
  527. // back to kernel32!TerminatProcess to force in.
  528. //
  529. // History: 2000-10-27 vtan created
  530. // --------------------------------------------------------------------------
  531. NTSTATUS CBadApplicationManager::TerminateForcibly (HANDLE hProcess)
  532. {
  533. NTSTATUS status;
  534. HANDLE hProcessTerminate;
  535. // Duplicate the process handle and request all the access required
  536. // to create a remote thread in the process.
  537. if (DuplicateHandle(GetCurrentProcess(),
  538. hProcess,
  539. GetCurrentProcess(),
  540. &hProcessTerminate,
  541. SYNCHRONIZE | PROCESS_TERMINATE | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE,
  542. FALSE,
  543. 0) != FALSE)
  544. {
  545. DWORD dwWaitResult;
  546. HANDLE hThread, hWaitArray[2];
  547. // Go and create the remote thread that immediately turns
  548. // around and calls kernel32!ExitProcess. This allows
  549. // a clean process shutdown to occur. If this times out
  550. // then kill the process with terminate process.
  551. status = RtlCreateUserThread(hProcessTerminate,
  552. NULL,
  553. FALSE,
  554. 0,
  555. 0,
  556. 0,
  557. reinterpret_cast<PUSER_THREAD_START_ROUTINE>(ExitProcess),
  558. NULL,
  559. &hThread,
  560. NULL);
  561. if (NT_SUCCESS(status))
  562. {
  563. hWaitArray[0] = hThread;
  564. hWaitArray[1] = hProcessTerminate;
  565. dwWaitResult = WaitForMultipleObjects(ARRAYSIZE(hWaitArray),
  566. hWaitArray,
  567. TRUE,
  568. 5000);
  569. TBOOL(CloseHandle(hThread));
  570. if (dwWaitResult != WAIT_TIMEOUT)
  571. {
  572. status = STATUS_SUCCESS;
  573. }
  574. else
  575. {
  576. status = STATUS_TIMEOUT;
  577. }
  578. }
  579. if (status != STATUS_SUCCESS)
  580. {
  581. if (TerminateProcess(hProcessTerminate, 0) != FALSE)
  582. {
  583. status = STATUS_SUCCESS;
  584. }
  585. else
  586. {
  587. status = CStatusCode::StatusCodeOfLastError();
  588. }
  589. }
  590. TBOOL(CloseHandle(hProcessTerminate));
  591. }
  592. else
  593. {
  594. status = CStatusCode::StatusCodeOfLastError();
  595. }
  596. return(status);
  597. }
  598. // --------------------------------------------------------------------------
  599. // CBadApplicationManager::TerminateGracefully
  600. //
  601. // Arguments: hProcess = Process to terminate.
  602. //
  603. // Returns: NTSTATUS
  604. //
  605. // Purpose: Creates a rundll32 process on the session of the target
  606. // process in WinSta0\Default which will re-enter this dll and
  607. // call the "terminate" functionality. This allows the process to
  608. // walk the window list corresponding to that session and send
  609. // those windows close messages and wait for graceful
  610. // termination.
  611. //
  612. // History: 2000-10-24 vtan created
  613. // --------------------------------------------------------------------------
  614. NTSTATUS CBadApplicationManager::TerminateGracefully (HANDLE hProcess)
  615. {
  616. NTSTATUS status;
  617. ULONG ulReturnLength;
  618. PROCESS_BASIC_INFORMATION processBasicInformation;
  619. status = NtQueryInformationProcess(hProcess,
  620. ProcessBasicInformation,
  621. &processBasicInformation,
  622. sizeof(processBasicInformation),
  623. &ulReturnLength);
  624. if (NT_SUCCESS(status))
  625. {
  626. HANDLE hToken;
  627. if (OpenProcessToken(hProcess,
  628. TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY,
  629. &hToken) != FALSE)
  630. {
  631. STARTUPINFOW startupInfo;
  632. PROCESS_INFORMATION processInformation;
  633. WCHAR szCommandLine[MAX_PATH];
  634. ZeroMemory(&startupInfo, sizeof(startupInfo));
  635. ZeroMemory(&processInformation, sizeof(processInformation));
  636. startupInfo.cb = sizeof(startupInfo);
  637. startupInfo.lpDesktop = const_cast<WCHAR*>(s_szDefaultDesktop);
  638. wsprintfW(szCommandLine, L"rundll32 shsvcs.dll,FUSCompatibilityEntry terminate %d", static_cast<DWORD>(processBasicInformation.UniqueProcessId));
  639. if (CreateProcessAsUserW(hToken,
  640. NULL,
  641. szCommandLine,
  642. NULL,
  643. NULL,
  644. FALSE,
  645. 0,
  646. NULL,
  647. NULL,
  648. &startupInfo,
  649. &processInformation) != FALSE)
  650. {
  651. DWORD dwWaitResult;
  652. HANDLE hArray[2];
  653. // Assume that this whole thing failed.
  654. status = STATUS_UNSUCCESSFUL;
  655. TBOOL(CloseHandle(processInformation.hThread));
  656. // Wait on both process objects. If the process to be terminated
  657. // is signaled then the rundll32 stub did its job. If the rundll32
  658. // stub is signaled then find out what its exit code is and either
  659. // continue waiting on the process to be terminated or return back
  660. // a code to the caller indicating success or failure. Failure
  661. // forces the process to be terminated abruptly.
  662. hArray[0] = hProcess;
  663. hArray[1] = processInformation.hProcess;
  664. dwWaitResult = WaitForMultipleObjects(ARRAYSIZE(hArray),
  665. hArray,
  666. FALSE,
  667. 10000);
  668. // If the process to be terminated is signaled then we're done.
  669. if (dwWaitResult == WAIT_OBJECT_0)
  670. {
  671. status = STATUS_SUCCESS;
  672. }
  673. // If the rundll32 stub is signaled then find out what it found.
  674. else if (dwWaitResult == WAIT_OBJECT_0 + 1)
  675. {
  676. DWORD dwExitCode;
  677. dwExitCode = STILL_ACTIVE;
  678. if (GetExitCodeProcess(processInformation.hProcess, &dwExitCode) != FALSE)
  679. {
  680. ASSERTMSG((dwExitCode == CGracefulTerminateApplication::NO_WINDOWS_FOUND) || (dwExitCode == CGracefulTerminateApplication::WAIT_WINDOWS_FOUND), "Unexpected process exit code in CBadApplicationManager::TerminateGracefully");
  681. // If the rundll32 stub says it found some windows then
  682. // wait for the process to terminate itself.
  683. if (dwExitCode == CGracefulTerminateApplication::WAIT_WINDOWS_FOUND)
  684. {
  685. // If the process terminates within the timeout period
  686. // then we're done.
  687. if (WaitForSingleObject(hProcess, 10000) == WAIT_OBJECT_0)
  688. {
  689. status = STATUS_SUCCESS;
  690. }
  691. }
  692. }
  693. }
  694. TBOOL(CloseHandle(processInformation.hProcess));
  695. }
  696. else
  697. {
  698. status = CStatusCode::StatusCodeOfLastError();
  699. }
  700. TBOOL(CloseHandle(hToken));
  701. }
  702. else
  703. {
  704. status = CStatusCode::StatusCodeOfLastError();
  705. }
  706. }
  707. return(status);
  708. }
  709. // --------------------------------------------------------------------------
  710. // CBadApplicationManager::Cleanup
  711. //
  712. // Arguments: <none>
  713. //
  714. // Returns: <none>
  715. //
  716. // Purpose: Releases used resources in the class. Used by both the
  717. // constructor and the thread - whoever wins.
  718. //
  719. // History: 2000-12-12 vtan created
  720. // --------------------------------------------------------------------------
  721. void CBadApplicationManager::Cleanup (void)
  722. {
  723. if (_fRegisteredNotification)
  724. {
  725. (BOOL)WinStationUnRegisterConsoleNotification(SERVERNAME_CURRENT, _hwnd);
  726. _fRegisteredNotification = false;
  727. }
  728. if (_hwnd != NULL)
  729. {
  730. TBOOL(DestroyWindow(_hwnd));
  731. _hwnd = NULL;
  732. }
  733. if (_atom != 0)
  734. {
  735. TBOOL(UnregisterClass(MAKEINTRESOURCE(_atom), _hInstance));
  736. _atom = 0;
  737. }
  738. }
  739. // --------------------------------------------------------------------------
  740. // CBadApplicationManager::Handle_Logon
  741. //
  742. // Arguments: <none>
  743. //
  744. // Returns: <none>
  745. //
  746. // Purpose: Nothing at present.
  747. //
  748. // History: 2000-10-24 vtan created
  749. // --------------------------------------------------------------------------
  750. void CBadApplicationManager::Handle_Logon (void)
  751. {
  752. }
  753. // --------------------------------------------------------------------------
  754. // CBadApplicationManager::Handle_Logoff
  755. //
  756. // Arguments: dwSessionID = Session ID that is logging off.
  757. //
  758. // Returns: <none>
  759. //
  760. // Purpose: Remove any restore processes we have in the list. The user
  761. // is logging off so they shouldn't come back. Releases the last
  762. // user to actively connect to the machine.
  763. //
  764. // History: 2000-10-24 vtan created
  765. // --------------------------------------------------------------------------
  766. void CBadApplicationManager::Handle_Logoff (DWORD dwSessionID)
  767. {
  768. int i;
  769. CSingleThreadedExecution listLock(_lock);
  770. for (i = _restoreApplications.GetCount() - 1; i >= 0; --i)
  771. {
  772. CRestoreApplication *pRestoreApplication;
  773. pRestoreApplication = static_cast<CRestoreApplication*>(_restoreApplications.Get(i));
  774. if ((pRestoreApplication != NULL) &&
  775. pRestoreApplication->IsEqualSessionID(dwSessionID))
  776. {
  777. TSTATUS(_restoreApplications.Remove(i));
  778. }
  779. }
  780. ReleaseHandle(_hTokenLastUser);
  781. }
  782. // --------------------------------------------------------------------------
  783. // CBadApplicationManager::Handle_Connect
  784. //
  785. // Arguments: dwSessionID = Session ID connecting.
  786. // hToken = Handle to token of user connecting.
  787. //
  788. // Returns: <none>
  789. //
  790. // Purpose: Handles BAM3. This is the save for restoration all processes
  791. // that use resources that aren't easily shared and restore all
  792. // processes that were saved which aren't easily shared.
  793. //
  794. // It's optimized for not closing the processes of the same user
  795. // should that user re-connect. This allows the screen saver to
  796. // kick in and return to welcome without killing the user's
  797. // processes unnecessarily.
  798. //
  799. // Also handles BAM4.
  800. //
  801. // History: 2000-10-24 vtan created
  802. // --------------------------------------------------------------------------
  803. void CBadApplicationManager::Handle_Connect (DWORD dwSessionID, HANDLE hToken)
  804. {
  805. if ((_hTokenLastUser != NULL) && (hToken != NULL))
  806. {
  807. PSID pSIDLastUser, pSIDCurrentUser;
  808. CTokenInformation tokenLastUser(_hTokenLastUser);
  809. CTokenInformation tokenCurrentUser(hToken);
  810. pSIDLastUser = tokenLastUser.GetUserSID();
  811. pSIDCurrentUser = tokenCurrentUser.GetUserSID();
  812. if ((pSIDLastUser != NULL) && (pSIDCurrentUser != NULL) && !EqualSid(pSIDLastUser, pSIDCurrentUser))
  813. {
  814. int i;
  815. DWORD dwSessionIDMatch;
  816. ULONG ulReturnLength;
  817. CRestoreApplication *pRestoreApplication;
  818. if (NT_SUCCESS(NtQueryInformationToken(_hTokenLastUser,
  819. TokenSessionId,
  820. &dwSessionIDMatch,
  821. sizeof(dwSessionIDMatch),
  822. &ulReturnLength)))
  823. {
  824. // Walk the _badApplications list.
  825. _lock.Acquire();
  826. i = _badApplications.GetCount() - 1;
  827. while (i >= 0)
  828. {
  829. BAD_APPLICATION_INFO badApplicationInfo;
  830. if (NT_SUCCESS(_badApplications.Get(&badApplicationInfo, i)))
  831. {
  832. bool fTerminateProcess;
  833. fTerminateProcess = false;
  834. // Look for BAM_TYPE_SWITCH_TO_NEW_USER_WITH_RESTORE processes
  835. // which have token session IDs that match the _hTokenLastUser
  836. // session ID. These processes must be terminated and added to
  837. // a list to be restarted on reconnection.
  838. if ((badApplicationInfo.bamType == BAM_TYPE_SWITCH_TO_NEW_USER_WITH_RESTORE) &&
  839. (badApplicationInfo.dwSessionID == dwSessionIDMatch))
  840. {
  841. pRestoreApplication = new CRestoreApplication;
  842. if (pRestoreApplication != NULL)
  843. {
  844. if (NT_SUCCESS(pRestoreApplication->GetInformation(badApplicationInfo.hProcess)))
  845. {
  846. TSTATUS(_restoreApplications.Add(pRestoreApplication));
  847. fTerminateProcess = true;
  848. }
  849. pRestoreApplication->Release();
  850. }
  851. }
  852. // Look for BAM_TYPE_SWITCH_TO_NEW_USER (even though this is
  853. // a connect/reconnect). Always kill these processes.
  854. if (badApplicationInfo.bamType == BAM_TYPE_SWITCH_TO_NEW_USER)
  855. {
  856. fTerminateProcess = true;
  857. }
  858. if (fTerminateProcess)
  859. {
  860. // In any case release the lock, kill the process
  861. // remove it from the watch list. Then reset the
  862. // index back to the end of the list. Make sure to
  863. // account for the "--i;" instruction below by not
  864. // decrementing by 1.
  865. _lock.Release();
  866. TSTATUS(PerformTermination(badApplicationInfo.hProcess, true));
  867. _lock.Acquire();
  868. TBOOL(CloseHandle(badApplicationInfo.hProcess));
  869. TSTATUS(_badApplications.Remove(i));
  870. i = _badApplications.GetCount();
  871. }
  872. }
  873. --i;
  874. }
  875. _lock.Release();
  876. }
  877. // Now walk the restore list looking for matches against the
  878. // connecting session ID. Restore these processes.
  879. _lock.Acquire();
  880. i = _restoreApplications.GetCount() - 1;
  881. while (i >= 0)
  882. {
  883. pRestoreApplication = static_cast<CRestoreApplication*>(_restoreApplications.Get(i));
  884. if ((pRestoreApplication != NULL) &&
  885. pRestoreApplication->IsEqualSessionID(dwSessionID))
  886. {
  887. HANDLE hProcess;
  888. _lock.Release();
  889. if (NT_SUCCESS(pRestoreApplication->Restore(&hProcess)))
  890. {
  891. CBadApplication badApplication(pRestoreApplication->GetCommandLine());
  892. TBOOL(CloseHandle(hProcess));
  893. }
  894. _lock.Acquire();
  895. TSTATUS(_restoreApplications.Remove(i));
  896. i = _restoreApplications.GetCount();
  897. }
  898. --i;
  899. }
  900. _lock.Release();
  901. }
  902. }
  903. if (hToken != NULL)
  904. {
  905. _dwSessionIDLastConnect = static_cast<DWORD>(-1);
  906. }
  907. else
  908. {
  909. _dwSessionIDLastConnect = dwSessionID;
  910. }
  911. }
  912. // --------------------------------------------------------------------------
  913. // CBadApplicationManager::Handle_Disconnect
  914. //
  915. // Arguments: dwSessionID = Session ID that is disconnecting.
  916. // hToken = Token of the user disconnecting.
  917. //
  918. // Returns: <none>
  919. //
  920. // Purpose: If the session isn't the same as the last connected session
  921. // then release the last user token and save the current one.
  922. //
  923. // History: 2000-10-24 vtan created
  924. // --------------------------------------------------------------------------
  925. void CBadApplicationManager::Handle_Disconnect (DWORD dwSessionID, HANDLE hToken)
  926. {
  927. if (_dwSessionIDLastConnect != dwSessionID)
  928. {
  929. ReleaseHandle(_hTokenLastUser);
  930. if (hToken != NULL)
  931. {
  932. TBOOL(DuplicateHandle(GetCurrentProcess(),
  933. hToken,
  934. GetCurrentProcess(),
  935. &_hTokenLastUser,
  936. 0,
  937. FALSE,
  938. DUPLICATE_SAME_ACCESS));
  939. }
  940. }
  941. }
  942. // --------------------------------------------------------------------------
  943. // CBadApplicationManager::Handle_WM_WTSSESSION_CHANGE
  944. //
  945. // Arguments: wParam = Type of session change.
  946. // lParam = Pointer to WTSSESSION_NOTIFICATION struct.
  947. //
  948. // Returns: LRESULT
  949. //
  950. // Purpose: Handles WM_WTSSESSION_CHANGE messages.
  951. //
  952. // History: 2000-10-23 vtan created
  953. // --------------------------------------------------------------------------
  954. LRESULT CBadApplicationManager::Handle_WM_WTSSESSION_CHANGE (WPARAM wParam, LPARAM lParam)
  955. {
  956. ULONG ulReturnLength;
  957. WINSTATIONUSERTOKEN winStationUserToken;
  958. winStationUserToken.ProcessId = reinterpret_cast<HANDLE>(GetCurrentProcessId());
  959. winStationUserToken.ThreadId = reinterpret_cast<HANDLE>(GetCurrentThreadId());
  960. winStationUserToken.UserToken = NULL;
  961. (BOOLEAN)WinStationQueryInformation(SERVERNAME_CURRENT,
  962. lParam,
  963. WinStationUserToken,
  964. &winStationUserToken,
  965. sizeof(winStationUserToken),
  966. &ulReturnLength);
  967. switch (wParam)
  968. {
  969. case WTS_SESSION_LOGOFF:
  970. Handle_Logoff(lParam);
  971. break;
  972. case WTS_SESSION_LOGON:
  973. Handle_Logon();
  974. // Fall thru to connect case.
  975. case WTS_CONSOLE_CONNECT:
  976. case WTS_REMOTE_CONNECT:
  977. Handle_Connect(lParam, winStationUserToken.UserToken);
  978. break;
  979. case WTS_CONSOLE_DISCONNECT:
  980. case WTS_REMOTE_DISCONNECT:
  981. Handle_Disconnect(lParam, winStationUserToken.UserToken);
  982. break;
  983. default:
  984. break;
  985. }
  986. if (winStationUserToken.UserToken != NULL)
  987. {
  988. TBOOL(CloseHandle(winStationUserToken.UserToken));
  989. }
  990. return(1);
  991. }
  992. // --------------------------------------------------------------------------
  993. // CBadApplicationManager::NotificationWindowProc
  994. //
  995. // Arguments: See the platform SDK under WindowProc.
  996. //
  997. // Returns: LRESULT
  998. //
  999. // Purpose: Handles messages for the Notification window.
  1000. //
  1001. // History: 2000-10-23 vtan created
  1002. // --------------------------------------------------------------------------
  1003. LRESULT CALLBACK CBadApplicationManager::NotificationWindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1004. {
  1005. LRESULT lResult;
  1006. CBadApplicationManager *pThis;
  1007. pThis = reinterpret_cast<CBadApplicationManager*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
  1008. switch (uMsg)
  1009. {
  1010. case WM_CREATE:
  1011. {
  1012. CREATESTRUCT *pCreateStruct;
  1013. pCreateStruct = reinterpret_cast<CREATESTRUCT*>(lParam);
  1014. (LONG_PTR)SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pCreateStruct->lpCreateParams));
  1015. lResult = 0;
  1016. break;
  1017. }
  1018. case WM_WTSSESSION_CHANGE:
  1019. lResult = pThis->Handle_WM_WTSSESSION_CHANGE(wParam, lParam);
  1020. break;
  1021. default:
  1022. lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
  1023. break;
  1024. }
  1025. return(lResult);
  1026. }
  1027. // --------------------------------------------------------------------------
  1028. // CBadApplicationManager::RegisterThreadProc
  1029. //
  1030. // Arguments: pParameter = Object pointer.
  1031. //
  1032. // Returns: DWORD
  1033. //
  1034. // Purpose: Opens the TermSrvReadyEvent and waits on it. Once ready it
  1035. // registers for a notifications.
  1036. //
  1037. // History: 2000-10-23 vtan created
  1038. // --------------------------------------------------------------------------
  1039. DWORD WINAPI CBadApplicationManager::RegisterThreadProc (void *pParameter)
  1040. {
  1041. int iCounter;
  1042. HANDLE hTermSrvReadyEvent;
  1043. HMODULE hModule;
  1044. CBadApplicationManager *pThis;
  1045. pThis = reinterpret_cast<CBadApplicationManager*>(pParameter);
  1046. hModule = pThis->_hModule;
  1047. ASSERTMSG(hModule != NULL, "NULL HMODULE in CBadApplicationManager::RegisterThreadProc");
  1048. iCounter = 0;
  1049. hTermSrvReadyEvent = OpenEvent(SYNCHRONIZE,
  1050. FALSE,
  1051. TEXT("TermSrvReadyEvent"));
  1052. while ((hTermSrvReadyEvent == NULL) && (iCounter < 60))
  1053. {
  1054. ++iCounter;
  1055. Sleep(1000);
  1056. hTermSrvReadyEvent = OpenEvent(SYNCHRONIZE,
  1057. FALSE,
  1058. TEXT("TermSrvReadyEvent"));
  1059. }
  1060. if (hTermSrvReadyEvent != NULL)
  1061. {
  1062. if (WaitForSingleObject(hTermSrvReadyEvent, 60000) == WAIT_OBJECT_0)
  1063. {
  1064. pThis->_fRegisteredNotification = (WinStationRegisterConsoleNotification(SERVERNAME_CURRENT, pThis->_hwnd, NOTIFY_FOR_ALL_SESSIONS) != FALSE);
  1065. }
  1066. TBOOL(CloseHandle(hTermSrvReadyEvent));
  1067. }
  1068. pThis->Release();
  1069. FreeLibraryAndExitThread(hModule, 0);
  1070. }
  1071. #endif /* _X86_ */