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.

651 lines
19 KiB

  1. /**************************** Module Header ********************************\
  2. * Module Name: exitwin.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * History:
  7. * 07-23-92 ScottLu Created.
  8. \***************************************************************************/
  9. #include "precomp.h"
  10. #pragma hdrstop
  11. #define OPTIONMASK (EWX_SHUTDOWN | EWX_REBOOT | EWX_FORCE)
  12. /*
  13. * Globals local to this file only
  14. */
  15. PWINDOWSTATION gpwinstaLogoff;
  16. DWORD gdwLocks;
  17. DWORD gdwShutdownFlags;
  18. HANDLE gpidEndSession;
  19. #ifdef PRERELEASE
  20. DWORD gdwllParamCopy, gdwStatusCopy, gdwFlagsCopy;
  21. BOOL gfNotifiedCopy;
  22. #endif // PRERELEASE
  23. /*
  24. * Called by ExitWindowsEx() to check whether the thread is permitted to logoff.
  25. * If it is, and this is WinLogon calling, then also save any of the user's
  26. * setting that have not yet been stored in the profile.
  27. */
  28. BOOL PrepareForLogoff(
  29. UINT uFlags)
  30. {
  31. PTHREADINFO ptiCurrent = PtiCurrent();
  32. CheckCritIn();
  33. if (ptiCurrent->TIF_flags & TIF_RESTRICTED) {
  34. PW32JOB pW32Job;
  35. pW32Job = ptiCurrent->ppi->pW32Job;
  36. UserAssert(pW32Job != NULL);
  37. if (pW32Job->restrictions & JOB_OBJECT_UILIMIT_EXITWINDOWS) {
  38. // Not permitted to ExitWindows.
  39. return FALSE;
  40. }
  41. }
  42. /*
  43. * There are no restrictions, or the restriction do not deny shutdown:
  44. * The caller is about to ExitWindowsEx via CSR, so save the volatile
  45. * elements of the User preferences in their profile
  46. */
  47. if (PsGetThreadProcessId(ptiCurrent->pEThread) == gpidLogon) {
  48. /*
  49. * Save the current user's NumLock state
  50. */
  51. TL tlName;
  52. PUNICODE_STRING pProfileUserName = CreateProfileUserName(&tlName);
  53. RegisterPerUserKeyboardIndicators(pProfileUserName);
  54. FreeProfileUserName(pProfileUserName, &tlName);
  55. }
  56. return TRUE;
  57. UNREFERENCED_PARAMETER(uFlags);
  58. }
  59. BOOL NotifyLogon(
  60. PWINDOWSTATION pwinsta,
  61. PLUID pluidCaller,
  62. DWORD dwFlags,
  63. NTSTATUS StatusCode)
  64. {
  65. BOOL fNotified = FALSE;
  66. DWORD dwllParam;
  67. DWORD dwStatus;
  68. if (!(dwFlags & EWX_NONOTIFY)) {
  69. if (dwFlags & EWX_CANCELED) {
  70. dwllParam = LOGON_LOGOFFCANCELED;
  71. dwStatus = StatusCode;
  72. } else {
  73. dwllParam = LOGON_LOGOFF;
  74. dwStatus = dwFlags;
  75. }
  76. if (dwFlags & EWX_SHUTDOWN) {
  77. /*
  78. * Post the message to the global logon notify window
  79. */
  80. if (gspwndLogonNotify != NULL) {
  81. _PostMessage(gspwndLogonNotify, WM_LOGONNOTIFY,
  82. dwllParam, (LONG)dwStatus);
  83. fNotified = TRUE;
  84. }
  85. } else {
  86. if (gspwndLogonNotify != NULL &&
  87. (RtlEqualLuid(&pwinsta->luidUser, pluidCaller) ||
  88. RtlEqualLuid(&luidSystem, pluidCaller))) {
  89. _PostMessage(gspwndLogonNotify, WM_LOGONNOTIFY, dwllParam,
  90. (LONG)dwStatus);
  91. fNotified = TRUE;
  92. }
  93. }
  94. }
  95. #ifdef PRERELEASE
  96. /*
  97. * Remember what these were for debugging purposes.
  98. */
  99. gdwllParamCopy = dwllParam;
  100. gdwFlagsCopy = dwFlags;
  101. gdwStatusCopy = dwStatus;
  102. gfNotifiedCopy = fNotified;
  103. #endif // PRERELEASE
  104. return fNotified;
  105. }
  106. NTSTATUS InitiateShutdown(
  107. PETHREAD Thread,
  108. PULONG lpdwFlags)
  109. {
  110. static PRIVILEGE_SET psShutdown = {
  111. 1, PRIVILEGE_SET_ALL_NECESSARY, { SE_SHUTDOWN_PRIVILEGE, 0 }
  112. };
  113. PEPROCESS Process;
  114. LUID luidCaller;
  115. PPROCESSINFO ppi;
  116. PWINDOWSTATION pwinsta;
  117. HWINSTA hwinsta;
  118. PTHREADINFO ptiClient;
  119. NTSTATUS Status;
  120. DWORD dwFlags;
  121. /*
  122. * Find out the callers sid. Only want to shutdown processes in the
  123. * callers sid.
  124. */
  125. Process = PsGetThreadProcess(Thread);
  126. ptiClient = PtiFromThread(Thread);
  127. Status = GetProcessLuid(Thread, &luidCaller);
  128. if (!NT_SUCCESS(Status)) {
  129. return Status;
  130. }
  131. /*
  132. * Set the system flag if the caller is a system process.
  133. * Winlogon uses this to determine in which context to perform
  134. * a shutdown operation.
  135. */
  136. dwFlags = *lpdwFlags;
  137. if (RtlEqualLuid(&luidCaller, &luidSystem)) {
  138. dwFlags |= EWX_SYSTEM_CALLER;
  139. } else {
  140. dwFlags &= ~EWX_SYSTEM_CALLER;
  141. }
  142. /*
  143. * Find a windowstation. If the process does not have one
  144. * assigned, use the standard one.
  145. */
  146. ppi = PpiFromProcess(Process);
  147. if (ppi == NULL) {
  148. /*
  149. * We ran into a case where the thread was terminated and had already
  150. * been cleaned up by USER. Thus, the ppi and ptiClient was NULL.
  151. */
  152. return STATUS_INVALID_HANDLE;
  153. }
  154. pwinsta = ppi->rpwinsta;
  155. hwinsta = ppi->hwinsta;
  156. /*
  157. * If we're not being called by Winlogon, validate the call and
  158. * notify the logon process to do the actual shutdown.
  159. */
  160. if (PsGetThreadProcessId(Thread) != gpidLogon) {
  161. dwFlags &= ~EWX_WINLOGON_CALLER;
  162. *lpdwFlags = dwFlags;
  163. if (pwinsta == NULL) {
  164. #ifndef LATER
  165. return STATUS_INVALID_HANDLE;
  166. #else
  167. hwinsta = ppi->pOpenObjectTable[HI_WINDOWSTATION].h;
  168. if (hwinsta == NULL) {
  169. return STATUS_INVALID_HANDLE;
  170. }
  171. pwinsta = (PWINDOWSTATION)ppi->pOpenObjectTable[HI_WINDOWSTATION].phead;
  172. #endif
  173. }
  174. /*
  175. * Check security first - does this thread have access?
  176. */
  177. if (!RtlAreAllAccessesGranted(ppi->amwinsta, WINSTA_EXITWINDOWS)) {
  178. return STATUS_ACCESS_DENIED;
  179. }
  180. /*
  181. * If the client requested shutdown, reboot, or poweroff they must have
  182. * the shutdown privilege.
  183. */
  184. if (dwFlags & EWX_SHUTDOWN) {
  185. if (!IsPrivileged(&psShutdown) ) {
  186. return STATUS_PRIVILEGE_NOT_HELD;
  187. }
  188. } else {
  189. /*
  190. * If this is a non-IO windowstation and we are not shutting down,
  191. * fail the call.
  192. */
  193. if (pwinsta->dwWSF_Flags & WSF_NOIO) {
  194. return STATUS_INVALID_DEVICE_REQUEST;
  195. }
  196. }
  197. }
  198. /*
  199. * Is there a shutdown already in progress?
  200. */
  201. if (gdwThreadEndSession != 0) {
  202. DWORD dwNew;
  203. /*
  204. * If the current shutdown in another sid and is not being done by
  205. * winlogon, override it.
  206. */
  207. if (!RtlEqualLuid(&luidCaller, &gpwinstaLogoff->luidEndSession) &&
  208. (gpidEndSession != gpidLogon)) {
  209. return STATUS_RETRY;
  210. }
  211. /*
  212. * Calculate new flags
  213. */
  214. dwNew = dwFlags & OPTIONMASK & (~gdwShutdownFlags);
  215. /*
  216. * Should we override the other shutdown? Make sure
  217. * winlogon does not recurse.
  218. */
  219. if (dwNew && HandleToUlong(PsGetCurrentThreadId()) !=
  220. gdwThreadEndSession) {
  221. /*
  222. * Only one windowstation can be logged off at a time.
  223. */
  224. if (!(dwFlags & EWX_SHUTDOWN) &&
  225. pwinsta != gpwinstaLogoff) {
  226. return STATUS_DEVICE_BUSY;
  227. }
  228. /* Bug# 453872
  229. * Since we are about to fail this call. Do not change gdwShutdownFlags
  230. * Later when we notify winlogon in EndShtdown, if we changed gdwShutdownFlags
  231. * and the call does not have EWX_WINLOGON_CALLER, winlogon will abort the call
  232. * to take care of the case when an application keeps calling ExitWindows.
  233. * [msadek- 08/08/2001]
  234. */
  235. #if 0
  236. /*
  237. * Set the new flags
  238. */
  239. gdwShutdownFlags = dwFlags;
  240. #endif
  241. if (dwNew & EWX_FORCE) {
  242. return STATUS_RETRY;
  243. } else {
  244. return STATUS_PENDING;
  245. }
  246. } else {
  247. /*
  248. * Don't override
  249. */
  250. return STATUS_PENDING;
  251. }
  252. }
  253. /*
  254. * If the caller is not winlogon, signal winlogon to start
  255. * the real shutdown.
  256. */
  257. if (PsGetThreadProcessId(Thread) != gpidLogon) {
  258. if (dwFlags & EWX_NOTIFY) {
  259. if (ptiClient && ptiClient->TIF_flags & TIF_16BIT)
  260. gptiShutdownNotify = ptiClient;
  261. dwFlags &= ~EWX_NOTIFY;
  262. *lpdwFlags = dwFlags;
  263. }
  264. if (NotifyLogon(pwinsta, &luidCaller, dwFlags, STATUS_SUCCESS))
  265. return STATUS_PENDING;
  266. else if (ptiClient && ptiClient->cWindows)
  267. return STATUS_CANT_WAIT;
  268. }
  269. /*
  270. * Mark this thread as the one that is currently processing
  271. * exit windows, and set the global saying someone is exiting
  272. */
  273. dwFlags |= EWX_WINLOGON_CALLER;
  274. *lpdwFlags = dwFlags;
  275. gdwShutdownFlags = dwFlags;
  276. gdwThreadEndSession = HandleToUlong(PsGetCurrentThreadId());
  277. SETSYSMETBOOL(SHUTTINGDOWN, TRUE);
  278. gpidEndSession = PsGetCurrentThreadProcessId();
  279. gpwinstaLogoff = pwinsta;
  280. pwinsta->luidEndSession = luidCaller;
  281. /*
  282. * Lock the windowstation to prevent apps from starting
  283. * while we're doing shutdown processing.
  284. */
  285. gdwLocks = pwinsta->dwWSF_Flags & (WSF_SWITCHLOCK | WSF_OPENLOCK);
  286. pwinsta->dwWSF_Flags |= (WSF_OPENLOCK | WSF_SHUTDOWN);
  287. /*
  288. * Set the flag WSF_REALSHUTDOWN if we are not doing just a
  289. * logoff
  290. */
  291. if (dwFlags &
  292. (EWX_WINLOGON_OLD_SHUTDOWN | EWX_WINLOGON_OLD_REBOOT |
  293. EWX_SHUTDOWN | EWX_REBOOT)) {
  294. pwinsta->dwWSF_Flags |= WSF_REALSHUTDOWN;
  295. }
  296. return STATUS_SUCCESS;
  297. }
  298. NTSTATUS EndShutdown(
  299. PETHREAD Thread,
  300. NTSTATUS StatusShutdown)
  301. {
  302. PWINDOWSTATION pwinsta = gpwinstaLogoff;
  303. PDESKTOP pdesk;
  304. LUID luidCaller;
  305. UserAssert(gpwinstaLogoff);
  306. gpwinstaLogoff = NULL;
  307. gpidEndSession = NULL;
  308. gdwThreadEndSession = 0;
  309. SETSYSMETBOOL(SHUTTINGDOWN, FALSE);
  310. pwinsta->dwWSF_Flags &= ~WSF_SHUTDOWN;
  311. if (!NT_SUCCESS(GetProcessLuid(Thread, &luidCaller))) {
  312. luidCaller = RtlConvertUlongToLuid(0); // null luid
  313. }
  314. if (!NT_SUCCESS(StatusShutdown)) {
  315. /*
  316. * We need to notify the process that called ExitWindows that
  317. * the logoff was aborted.
  318. */
  319. if (gptiShutdownNotify) {
  320. _PostThreadMessage(gptiShutdownNotify, WM_ENDSESSION, FALSE, 0);
  321. gptiShutdownNotify = NULL;
  322. }
  323. /*
  324. * Reset the windowstation lock flags so apps can start
  325. * again.
  326. */
  327. pwinsta->dwWSF_Flags =
  328. (pwinsta->dwWSF_Flags & ~WSF_OPENLOCK) |
  329. gdwLocks;
  330. /*
  331. * Bug 294204 - joejo
  332. * Tell winlogon that we we cancelled shutdown/logoff.
  333. */
  334. NotifyLogon(pwinsta, &luidCaller, gdwShutdownFlags | EWX_CANCELED, StatusShutdown);
  335. return STATUS_SUCCESS;
  336. }
  337. gptiShutdownNotify = NULL;
  338. /*
  339. * If logoff is occuring for the user set by winlogon, perform
  340. * the normal logoff cleanup. Otherwise, clear the open lock
  341. * and continue.
  342. */
  343. if (((pwinsta->luidUser.LowPart != 0) || (pwinsta->luidUser.HighPart != 0)) &&
  344. RtlEqualLuid(&pwinsta->luidUser, &luidCaller)) {
  345. /*
  346. * Zero out the free blocks in all desktop heaps.
  347. */
  348. for (pdesk = pwinsta->rpdeskList; pdesk != NULL; pdesk = pdesk->rpdeskNext) {
  349. RtlZeroHeap(Win32HeapGetHandle(pdesk->pheapDesktop), 0);
  350. }
  351. /*
  352. * Logoff/shutdown was successful. In case this is a logoff, remove
  353. * everything from the clipboard so the next logged on user can't get
  354. * at this stuff.
  355. */
  356. ForceEmptyClipboard(pwinsta);
  357. /*
  358. * Destroy all non-pinned atoms in the global atom table. User can't
  359. * create pinned atoms. Currently only the OLE atoms are pinned.
  360. */
  361. RtlEmptyAtomTable(pwinsta->pGlobalAtomTable, FALSE);
  362. // this code path is hit only on logoff and also on shutdown
  363. // We do not want to unload fonts twice when we attempt shutdown
  364. // so we mark that the fonts have been unloaded at a logoff time
  365. if (TEST_PUDF(PUDF_FONTSARELOADED)) {
  366. LeaveCrit();
  367. GreRemoveAllButPermanentFonts();
  368. EnterCrit();
  369. CLEAR_PUDF(PUDF_FONTSARELOADED);
  370. }
  371. } else {
  372. pwinsta->dwWSF_Flags &= ~WSF_OPENLOCK;
  373. }
  374. /*
  375. * Tell winlogon that we successfully shutdown/logged off.
  376. */
  377. NotifyLogon(pwinsta, &luidCaller, gdwShutdownFlags, STATUS_SUCCESS);
  378. return STATUS_SUCCESS;
  379. }
  380. /***************************************************************************\
  381. * xxxClientShutdown2
  382. *
  383. * Called by xxxClientShutdown
  384. \***************************************************************************/
  385. LONG xxxClientShutdown2(
  386. PBWL pbwl,
  387. UINT msg,
  388. WPARAM wParam)
  389. {
  390. HWND *phwnd;
  391. PWND pwnd;
  392. TL tlpwnd;
  393. BOOL fEnd;
  394. PTHREADINFO ptiCurrent = PtiCurrent();
  395. BOOL fDestroyTimers;
  396. LPARAM lParam;
  397. /*
  398. * Make sure we don't send this window any more WM_TIMER
  399. * messages if the session is ending. This was causing
  400. * AfterDark to fault when it freed some memory on the
  401. * WM_ENDSESSION and then tried to reference it on the
  402. * WM_TIMER.
  403. * LATER GerardoB: Do we still need to do this??
  404. * Do this horrible thing only if the process is in the
  405. * context being logged off.
  406. * Perhaps someday we should post a WM_CLOSE so the app
  407. * gets a better chance to clean up (if this process is in
  408. * the context being logged off, winsrv is going to call
  409. * TerminateProcess soon after this).
  410. */
  411. fDestroyTimers = (wParam & WMCS_EXIT) && (wParam & WMCS_CONTEXTLOGOFF);
  412. /*
  413. * fLogOff and fEndSession parameters (WM_ENDSESSION only)
  414. */
  415. lParam = wParam & ENDSESSION_LOGOFF;
  416. wParam &= WMCS_EXIT;
  417. /*
  418. * Now enumerate these windows and send the WM_QUERYENDSESSION or
  419. * WM_ENDSESSION messages.
  420. */
  421. for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) {
  422. if ((pwnd = RevalidateHwnd(*phwnd)) == NULL)
  423. continue;
  424. ThreadLockAlways(pwnd, &tlpwnd);
  425. /*
  426. * Send the message.
  427. */
  428. switch (msg) {
  429. case WM_QUERYENDSESSION:
  430. /*
  431. * Windows does not send the WM_QUERYENDSESSION to the app
  432. * that called ExitWindows
  433. */
  434. if (ptiCurrent == gptiShutdownNotify) {
  435. fEnd = TRUE;
  436. } else {
  437. fEnd = (xxxSendMessage(pwnd, WM_QUERYENDSESSION, FALSE, lParam) != 0);
  438. if (!fEnd) {
  439. RIPMSG2(RIP_WARNING, "xxxClientShutdown2: pwnd:%p canceled shutdown. lParam:%p",
  440. pwnd, lParam);
  441. }
  442. }
  443. break;
  444. case WM_ENDSESSION:
  445. xxxSendMessage(pwnd, WM_ENDSESSION, wParam, lParam);
  446. fEnd = TRUE;
  447. if (fDestroyTimers) {
  448. DestroyWindowsTimers(pwnd);
  449. }
  450. break;
  451. }
  452. ThreadUnlock(&tlpwnd);
  453. if (!fEnd)
  454. return WMCSR_CANCEL;
  455. }
  456. return WMCSR_ALLOWSHUTDOWN;
  457. }
  458. /***************************************************************************\
  459. * xxxClientShutdown
  460. *
  461. * This is the processing that occurs when an application receives a
  462. * WM_CLIENTSHUTDOWN message.
  463. *
  464. * 10-01-92 ScottLu Created.
  465. \***************************************************************************/
  466. LONG xxxClientShutdown(
  467. PWND pwnd,
  468. WPARAM wParam)
  469. {
  470. PBWL pbwl;
  471. PTHREADINFO ptiT;
  472. LONG lRet;
  473. /*
  474. * Build a list of windows first.
  475. */
  476. ptiT = GETPTI(pwnd);
  477. if ((pbwl = BuildHwndList(ptiT->rpdesk->pDeskInfo->spwnd->spwndChild,
  478. BWL_ENUMLIST, ptiT)) == NULL) {
  479. /*
  480. * Can't allocate memory to notify this thread's windows of shutdown.
  481. * Can't do more than kill the app
  482. */
  483. return WMCSR_ALLOWSHUTDOWN;
  484. }
  485. if (wParam & WMCS_QUERYEND) {
  486. lRet = xxxClientShutdown2(pbwl, WM_QUERYENDSESSION, wParam);
  487. } else {
  488. xxxClientShutdown2(pbwl, WM_ENDSESSION, wParam);
  489. lRet = WMCSR_DONE;
  490. }
  491. FreeHwndList(pbwl);
  492. return lRet;
  493. }
  494. /***************************************************************************\
  495. * xxxRegisterUserHungAppHandlers
  496. *
  497. * This routine simply records the WOW callback address for notification of
  498. * "hung" wow apps.
  499. *
  500. * History:
  501. * 01-Apr-1992 jonpa Created.
  502. * Added saving and duping of wowexc event handle
  503. \***************************************************************************/
  504. BOOL xxxRegisterUserHungAppHandlers(
  505. PFNW32ET pfnW32EndTask,
  506. HANDLE hEventWowExec)
  507. {
  508. BOOL bRetVal;
  509. PPROCESSINFO ppi;
  510. PWOWPROCESSINFO pwpi;
  511. ULONG ProcessInfo;
  512. NTSTATUS Status;
  513. //
  514. // Check the Target Process to see if this is a 16-bit process
  515. //
  516. Status = ZwQueryInformationProcess( NtCurrentProcess(),
  517. ProcessWx86Information,
  518. &ProcessInfo,
  519. sizeof(ProcessInfo),
  520. NULL
  521. );
  522. if (!NT_SUCCESS(Status) || ProcessInfo == 0) {
  523. return FALSE;
  524. }
  525. //
  526. // Allocate the per wow process info stuff
  527. // ensuring the memory is Zero init.
  528. //
  529. pwpi = (PWOWPROCESSINFO) UserAllocPoolWithQuotaZInit(
  530. sizeof(WOWPROCESSINFO), TAG_WOWPROCESSINFO);
  531. if (!pwpi)
  532. return FALSE;
  533. //
  534. // Reference the WowExec event for kernel access
  535. //
  536. bRetVal = NT_SUCCESS(ObReferenceObjectByHandle(
  537. hEventWowExec,
  538. EVENT_ALL_ACCESS,
  539. *ExEventObjectType,
  540. UserMode,
  541. &pwpi->pEventWowExec,
  542. NULL
  543. ));
  544. //
  545. // if sucess then intialize the pwpi, ppi structs
  546. // else free allocated memory
  547. //
  548. if (bRetVal) {
  549. pwpi->hEventWowExecClient = hEventWowExec;
  550. pwpi->lpfnWowExitTask = pfnW32EndTask;
  551. ppi = PpiCurrent();
  552. ppi->pwpi = pwpi;
  553. // add to the list, order doesn't matter
  554. pwpi->pwpiNext = gpwpiFirstWow;
  555. gpwpiFirstWow = pwpi;
  556. }
  557. else {
  558. UserFreePool(pwpi);
  559. }
  560. return bRetVal;
  561. }