Leaked source code of windows server 2003
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.

406 lines
14 KiB

  1. // --------------------------------------------------------------------------
  2. // Module Name: WaitInteractiveReady.cpp
  3. //
  4. // Copyright (c) 2001, Microsoft Corporation
  5. //
  6. // Class to handle waiting on the shell signal the desktop switch.
  7. //
  8. // History: 2001-01-15 vtan created
  9. // --------------------------------------------------------------------------
  10. #include "StandardHeader.h"
  11. #include "WaitInteractiveReady.h"
  12. #include <ginaipc.h>
  13. #include <msginaexports.h>
  14. #include "Impersonation.h"
  15. #include "StatusCode.h"
  16. // --------------------------------------------------------------------------
  17. // CWaitInteractiveReady::s_pWlxContext
  18. // CWaitInteractiveReady::s_hWait
  19. // CWaitInteractiveReady::s_hEvent
  20. // CWaitInteractiveReady::s_hEventShellReady
  21. // CWaitInteractiveReady::s_szEventName
  22. //
  23. // Purpose: Static member variables.
  24. //
  25. // History: 2001-01-15 vtan created
  26. // --------------------------------------------------------------------------
  27. HANDLE CWaitInteractiveReady::s_hWait = NULL;
  28. CWaitInteractiveReady* CWaitInteractiveReady::s_pWaitInteractiveReady = NULL;
  29. HANDLE CWaitInteractiveReady::s_hEventShellReady = NULL;
  30. const TCHAR CWaitInteractiveReady::s_szEventName[] = TEXT("msgina: ShellReadyEvent");
  31. // --------------------------------------------------------------------------
  32. // CWaitInteractiveReady::CWaitInteractiveReady
  33. //
  34. // Arguments: pWlxContext = PGLOBALS struct for msgina.
  35. //
  36. // Returns: <none>
  37. //
  38. // Purpose: Private constructor for this class. Create a synchronization
  39. // event for callback state determination.
  40. //
  41. // History: 2001-07-17 vtan created
  42. // --------------------------------------------------------------------------
  43. CWaitInteractiveReady::CWaitInteractiveReady (void *pWlxContext) :
  44. _pWlxContext(pWlxContext),
  45. _hEvent(CreateEvent(NULL, TRUE, FALSE, NULL))
  46. {
  47. }
  48. // --------------------------------------------------------------------------
  49. // CWaitInteractiveReady::~CWaitInteractiveReady
  50. //
  51. // Arguments: <none>
  52. //
  53. // Returns: <none>
  54. //
  55. // Purpose: Destructor. Clears member variables.
  56. //
  57. // History: 2001-07-17 vtan created
  58. // --------------------------------------------------------------------------
  59. CWaitInteractiveReady::~CWaitInteractiveReady (void)
  60. {
  61. ReleaseHandle(_hEvent);
  62. _pWlxContext = NULL;
  63. }
  64. // --------------------------------------------------------------------------
  65. // CWaitInteractiveReady::Create
  66. //
  67. // Arguments: pWlxContext = PGLOBALS struct for msgina.
  68. //
  69. // Returns: NTSTATUS
  70. //
  71. // Purpose: Creates resources required to manage switching desktops when
  72. // the shell signals the interactive ready event. This allows
  73. // the shell to be brought up in an interactive state.
  74. //
  75. // History: 2001-01-15 vtan created
  76. // --------------------------------------------------------------------------
  77. NTSTATUS CWaitInteractiveReady::Create (void *pWlxContext)
  78. {
  79. NTSTATUS status;
  80. HANDLE hToken;
  81. ASSERTMSG(s_hWait == NULL, "Wait already registered in CWaitInteractiveReady::Start");
  82. ASSERTMSG(s_hEventShellReady == NULL, "Named event already exists in CWaitInteractiveReady::Start");
  83. hToken = _Gina_GetUserToken(pWlxContext);
  84. if (hToken != NULL)
  85. {
  86. CImpersonation impersonation(hToken);
  87. if (impersonation.IsImpersonating())
  88. {
  89. s_hEventShellReady = CreateEvent(NULL, TRUE, FALSE, s_szEventName);
  90. if (s_hEventShellReady != NULL)
  91. {
  92. status = STATUS_SUCCESS;
  93. }
  94. else
  95. {
  96. status = CStatusCode::StatusCodeOfLastError();
  97. TSTATUS(ReleaseEvent());
  98. }
  99. }
  100. else
  101. {
  102. status = STATUS_BAD_IMPERSONATION_LEVEL;
  103. }
  104. }
  105. else
  106. {
  107. status = STATUS_NO_TOKEN;
  108. }
  109. return(status);
  110. }
  111. // --------------------------------------------------------------------------
  112. // CWaitInteractiveReady::Register
  113. //
  114. // Arguments: <none>
  115. //
  116. // Returns: NTSTATUS
  117. //
  118. // Purpose: Checks the state of the event being waited on. It's possible
  119. // that explorer may have already signaled this event before this
  120. // code is executed. If the event is signaled then CB_ShellReady
  121. // has already been called.
  122. //
  123. // History: 2001-07-16 vtan created
  124. // --------------------------------------------------------------------------
  125. NTSTATUS CWaitInteractiveReady::Register (void *pWlxContext)
  126. {
  127. NTSTATUS status;
  128. ASSERTMSG(s_hWait == NULL, "Wait already registered in CWaitInteractiveReady::Check");
  129. // Check and Stop should not be called from any thread other than
  130. // the main thread of winlogon. It's called in only a few places.
  131. // Firstly check the named event (msgina: ShellReadyEvent).
  132. if (s_hEventShellReady != NULL)
  133. {
  134. // If it exists then check to see if it's signaled.
  135. if (WaitForSingleObject(s_hEventShellReady, 0) == WAIT_OBJECT_0)
  136. {
  137. // If it's signaled then release the resources and return
  138. // a failure code (force it down the classic UI path).
  139. TSTATUS(ReleaseEvent());
  140. status = STATUS_UNSUCCESSFUL;
  141. }
  142. else
  143. {
  144. CWaitInteractiveReady *pWaitInteractiveReady;
  145. pWaitInteractiveReady = new CWaitInteractiveReady(pWlxContext);
  146. if (pWaitInteractiveReady != NULL)
  147. {
  148. if (pWaitInteractiveReady->IsCreated())
  149. {
  150. // Otherwise if it's not signaled then register a wait on
  151. // the named object for 30 seconds.
  152. if (RegisterWaitForSingleObject(&s_hWait,
  153. s_hEventShellReady,
  154. CB_ShellReady,
  155. pWaitInteractiveReady,
  156. 30000,
  157. WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE) == FALSE)
  158. {
  159. status = CStatusCode::StatusCodeOfLastError();
  160. delete pWaitInteractiveReady;
  161. TSTATUS(ReleaseEvent());
  162. }
  163. else
  164. {
  165. s_pWaitInteractiveReady = pWaitInteractiveReady;
  166. status = STATUS_SUCCESS;
  167. }
  168. }
  169. else
  170. {
  171. delete pWaitInteractiveReady;
  172. TSTATUS(ReleaseEvent());
  173. status = STATUS_NO_MEMORY;
  174. }
  175. }
  176. else
  177. {
  178. TSTATUS(ReleaseEvent());
  179. status = STATUS_NO_MEMORY;
  180. }
  181. }
  182. }
  183. else
  184. {
  185. status = STATUS_UNSUCCESSFUL;
  186. }
  187. return(status);
  188. }
  189. // --------------------------------------------------------------------------
  190. // CWaitInteractiveReady::Cancel
  191. //
  192. // Arguments: <none>
  193. //
  194. // Returns: NTSTATUS
  195. //
  196. // Purpose: Removes the wait on the interactive ready object. This is
  197. // done when a user causes a return to welcome. This is
  198. // necessary because if the callback fires AFTER the return to
  199. // welcome we will switch to the user's desktop which violates
  200. // security.
  201. //
  202. // History: 2001-01-15 vtan created
  203. // --------------------------------------------------------------------------
  204. NTSTATUS CWaitInteractiveReady::Cancel (void)
  205. {
  206. HANDLE hWait;
  207. // Grab the global hWait. If somebody beat us to this or it
  208. // didn't exist then there's nothing to do.
  209. hWait = InterlockedExchangePointer(&s_hWait, NULL);
  210. if (hWait != NULL)
  211. {
  212. CWaitInteractiveReady *pThis;
  213. // Grab the s_pWaitInteractiveReady. This is a pointer to the callback
  214. // memory. It will be valid unless the callback has already interlocked
  215. // the variable itself which means the callback has reached the determined
  216. // p0int already anyway and no wait is necessary.
  217. pThis = static_cast<CWaitInteractiveReady*>(InterlockedExchangePointer(reinterpret_cast<void**>(&s_pWaitInteractiveReady), NULL));
  218. // Try to unregister the wait. If this fails then the callback
  219. // is being executed. Wait until the callback reaches a determined
  220. // point (it will signal the internal event). Wait TWO minutes for
  221. // this. We cannot block the main thread of winlogon. If everything
  222. // is working nicely then this will be a no-brainer wait.
  223. if (UnregisterWait(hWait) == FALSE)
  224. {
  225. // If the unregister fails then wait if there's a valid event
  226. // to wait on - reasons explained above.
  227. if (pThis != NULL)
  228. {
  229. (DWORD)WaitForSingleObject(pThis->_hEvent, 120000);
  230. }
  231. }
  232. else
  233. {
  234. // Otherwise the wait was successfully unregistered indicating the
  235. // callback is not executing. Release the memory that was allocated
  236. // for it because it's not going to execute now.
  237. if (pThis != NULL)
  238. {
  239. delete pThis;
  240. }
  241. }
  242. }
  243. // Always release the wait handle. This is valid because if the callback
  244. // is executing and it grabbed the s_hWait then it will be NULL and it will
  245. // also try to release the event handle. If there was no s_hWait then we
  246. // just release the event handle anyway. Otherwise we grabbed the s_hWait
  247. // above and can release the event handle as well.
  248. TSTATUS(ReleaseEvent());
  249. return(STATUS_SUCCESS);
  250. }
  251. // --------------------------------------------------------------------------
  252. // CWaitInteractiveReady::IsCreated
  253. //
  254. // Arguments: <none>
  255. //
  256. // Returns: bool
  257. //
  258. // Purpose: Returns whether the object is successfully created.
  259. //
  260. // History: 2001-07-17 vtan created
  261. // --------------------------------------------------------------------------
  262. bool CWaitInteractiveReady::IsCreated (void) const
  263. {
  264. return(_hEvent != NULL);
  265. }
  266. // --------------------------------------------------------------------------
  267. // CWaitInteractiveReady::ReleaseEvent
  268. //
  269. // Arguments: <none>
  270. //
  271. // Returns: NTSTATUS
  272. //
  273. // Purpose: Resets the static member variables to the uninitialized
  274. // state.
  275. //
  276. // History: 2001-01-15 vtan created
  277. // --------------------------------------------------------------------------
  278. NTSTATUS CWaitInteractiveReady::ReleaseEvent (void)
  279. {
  280. HANDLE h;
  281. h = InterlockedExchangePointer(&s_hEventShellReady, NULL);
  282. if (h != NULL)
  283. {
  284. TBOOL(CloseHandle(h));
  285. }
  286. return(STATUS_SUCCESS);
  287. }
  288. // --------------------------------------------------------------------------
  289. // CWaitInteractiveReady::CB_ShellReady
  290. //
  291. // Arguments: pParameter = User callback parameter.
  292. // TimerOrWaitFired = Timer or wait fired.
  293. //
  294. // Returns: <none>
  295. //
  296. // Purpose: Invoked when the interactive ready event is signaled by the
  297. // shell. Switch the desktop to the user's desktop.
  298. //
  299. // History: 2001-01-15 vtan created
  300. // --------------------------------------------------------------------------
  301. void CALLBACK CWaitInteractiveReady::CB_ShellReady (void *pParameter, BOOLEAN TimerOrWaitFired)
  302. {
  303. UNREFERENCED_PARAMETER(TimerOrWaitFired);
  304. HANDLE hWait;
  305. CWaitInteractiveReady *pThis;
  306. pThis = static_cast<CWaitInteractiveReady*>(pParameter);
  307. // Wrap the desktop manipulation around a scope which saves and restores
  308. // the desktop. _Gina_SwitchDesktopToUser will set the thread's desktop
  309. // to \Default and will NOT restore it. This scoped object will restore it.
  310. if (pThis->_pWlxContext != NULL)
  311. {
  312. CDesktop desktop;
  313. // Hide the status host. Switch the desktops.
  314. _ShellStatusHostEnd(HOST_END_HIDE);
  315. (int)_Gina_SwitchDesktopToUser(pThis->_pWlxContext);
  316. }
  317. // Signal the internal event.
  318. TBOOL(SetEvent(pThis->_hEvent));
  319. // Grab the global hWait. If somebody beat us to it then they're trying
  320. // to stop this from happening. They could beat us to it at any time from
  321. // the invokation of the callback to here. That thread will wait for this
  322. // one to signal the internal event. In that case there's no work for this
  323. // thread. The owner of the hWait has to clean up. If this thread gets the
  324. // hWait then unregister the wait and release the resources.
  325. hWait = InterlockedExchangePointer(&s_hWait, NULL);
  326. if (hWait != NULL)
  327. {
  328. (BOOL)UnregisterWait(hWait);
  329. TSTATUS(ReleaseEvent());
  330. }
  331. // Interlock the s_pWaitInteractiveReady variable which is also an
  332. // indicator of having reached the determined point in the callback.
  333. (CWaitInteractiveReady*)InterlockedExchangePointer(reinterpret_cast<void**>(&s_pWaitInteractiveReady), NULL);
  334. // Delete our blob of data.
  335. delete pThis;
  336. }