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.

601 lines
20 KiB

  1. // --------------------------------------------------------------------------
  2. // Module Name: PowerButton.cpp
  3. //
  4. // Copyright (c) 2000, Microsoft Corporation
  5. //
  6. // Implementation file for CPowerButton class which handles the ACPI power
  7. // button.
  8. //
  9. // History: 2000-04-17 vtan created
  10. // --------------------------------------------------------------------------
  11. #include "StandardHeader.h"
  12. #include "PowerButton.h"
  13. #include <msginaexports.h>
  14. #include <shlobj.h>
  15. #include <shlobjp.h>
  16. #include <shellapi.h>
  17. #include <shlapip.h>
  18. #include <winsta.h>
  19. #include <ginarcid.h>
  20. #include "DimmedWindow.h"
  21. #include "Impersonation.h"
  22. #include "PrivilegeEnable.h"
  23. #include "SystemSettings.h"
  24. #define WM_HIDEOURSELVES (WM_USER + 10000)
  25. #define WM_READY (WM_USER + 10001)
  26. // --------------------------------------------------------------------------
  27. // CPowerButton::CPowerButton
  28. //
  29. // Arguments: pWlxContext = PGLOBALS allocated at WlxInitialize.
  30. // hDllInstance = HINSTANCE of the hosting DLL or EXE.
  31. //
  32. // Returns: <none>
  33. //
  34. // Purpose: Constructor for the CPowerButton class. It opens the effective
  35. // token of the caller (which is actually impersonating the
  36. // current user) for assignment in its thread token when
  37. // execution begins. The token cannot be assigned now because
  38. // the current thread is impersonating the user context and it
  39. // cannot assign the token to the newly created thread running in
  40. // the SYSTEM context.
  41. //
  42. // History: 2000-04-18 vtan created
  43. // --------------------------------------------------------------------------
  44. CPowerButton::CPowerButton (void *pWlxContext, HINSTANCE hDllInstance) :
  45. CThread(),
  46. _pWlxContext(pWlxContext),
  47. _hDllInstance(hDllInstance),
  48. _hToken(NULL),
  49. _pTurnOffDialog(NULL),
  50. _fCleanCompletion(true)
  51. {
  52. (BOOL)OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, FALSE, &_hToken);
  53. Resume();
  54. }
  55. // --------------------------------------------------------------------------
  56. // CPowerButton::~CPowerButton
  57. //
  58. // Arguments: <none>
  59. //
  60. // Returns: <none>
  61. //
  62. // Purpose: Destructor for the CPowerButton class. Cleans up resources
  63. // used by the class.
  64. //
  65. // History: 2000-04-18 vtan created
  66. // --------------------------------------------------------------------------
  67. CPowerButton::~CPowerButton (void)
  68. {
  69. ASSERTMSG(_pTurnOffDialog == NULL, "_pTurnOffDialog is not NULL in CPowerButton::~CPowerButton");
  70. ReleaseHandle(_hToken);
  71. }
  72. // --------------------------------------------------------------------------
  73. // CPowerButton::IsValidExecutionCode
  74. //
  75. // Arguments: dwGinaCode
  76. //
  77. // Returns: bool
  78. //
  79. // Purpose: Returns whether the given MSGINA_DLG_xxx code is valid. It
  80. // does fully verify the validity of the MSGINA_DLG_xxx_FLAG
  81. // options.
  82. //
  83. // History: 2000-06-06 vtan created
  84. // --------------------------------------------------------------------------
  85. bool CPowerButton::IsValidExecutionCode (DWORD dwGinaCode)
  86. {
  87. DWORD dwExecutionCode;
  88. dwExecutionCode = dwGinaCode & ~MSGINA_DLG_FLAG_MASK;
  89. return((dwExecutionCode == MSGINA_DLG_USER_LOGOFF) ||
  90. (dwExecutionCode == MSGINA_DLG_SHUTDOWN) ||
  91. (dwExecutionCode == MSGINA_DLG_DISCONNECT));
  92. }
  93. // --------------------------------------------------------------------------
  94. // CPowerButton::Entry
  95. //
  96. // Arguments: <none>
  97. //
  98. // Returns: DWORD
  99. //
  100. // Purpose: Main function of the thread. Change the thread's desktop first
  101. // in case the actual input desktop is Winlogon's which is the
  102. // secure desktop. Then change the thread's token so that the
  103. // user's privileges are respected in the action choices. This
  104. // actually isn't critical because the physical button on the
  105. // keyboard is pressed which means they can physically remove the
  106. // power also!
  107. //
  108. // History: 2000-04-18 vtan created
  109. // --------------------------------------------------------------------------
  110. DWORD CPowerButton::Entry (void)
  111. {
  112. DWORD dwResult;
  113. HDESK hDeskInput;
  114. CDesktop desktop;
  115. dwResult = MSGINA_DLG_FAILURE;
  116. // Get the input desktop.
  117. hDeskInput = OpenInputDesktop(0, FALSE, MAXIMUM_ALLOWED);
  118. if (hDeskInput != NULL)
  119. {
  120. bool fHandled;
  121. DWORD dwLengthNeeded;
  122. TCHAR szDesktopName[256];
  123. fHandled = false;
  124. // Get the desktop's name.
  125. if (GetUserObjectInformation(hDeskInput,
  126. UOI_NAME,
  127. szDesktopName,
  128. sizeof(szDesktopName),
  129. &dwLengthNeeded) != FALSE)
  130. {
  131. // If the desktop is "Winlogon" (case insensitive) then
  132. // assume that the secure desktop is showing. It's safe
  133. // to display the dialog and handle it inline.
  134. if (lstrcmpi(szDesktopName, TEXT("winlogon")) == 0)
  135. {
  136. dwResult = ShowDialog();
  137. fHandled = true;
  138. }
  139. else
  140. {
  141. CDesktop desktopTemp;
  142. // The input desktop is something else. Check the name.
  143. // If it's "Default" (case insensitive) then assume that
  144. // explorer is going to handle this message. Go find explorer's
  145. // tray window. Check it's not hung by probing with a
  146. // SendMessageTimeout. If that shows it's not hung then
  147. // send it the real message. If it's hung then don't let
  148. // explorer process this message. Instead handle it
  149. // internally with the funky desktop switch stuff.
  150. if (NT_SUCCESS(desktopTemp.SetInput()))
  151. {
  152. HWND hwnd;
  153. hwnd = FindWindow(TEXT("Shell_TrayWnd"), NULL);
  154. if (hwnd != NULL)
  155. {
  156. DWORD dwProcessID;
  157. DWORD_PTR dwUnused;
  158. (DWORD)GetWindowThreadProcessId(hwnd, &dwProcessID);
  159. if (SendMessageTimeout(hwnd, WM_NULL, 0, 0, SMTO_NORMAL, 500, &dwUnused) != 0)
  160. {
  161. // Before asking explorer to bring up the dialog
  162. // allow it to set the foreground window. We have
  163. // this power because win32k gave it to us when
  164. // the ACPI power button message was sent to winlogon.
  165. (BOOL)AllowSetForegroundWindow(dwProcessID);
  166. (LRESULT)SendMessage(hwnd, WM_CLOSE, 0, 0);
  167. fHandled = true;
  168. }
  169. }
  170. }
  171. }
  172. }
  173. // If the request couldn't be handled then switch the desktop to
  174. // winlogon's desktop and handle it here. This secures the dialog
  175. // on the secure desktop from rogue processes sending bogus messages
  176. // and crashing processes. The input desktop is required to be
  177. // switched. If this fails there's little that can be done. Ignore
  178. // this gracefully.
  179. if (!fHandled)
  180. {
  181. if (SwitchDesktop(GetThreadDesktop(GetCurrentThreadId())) != FALSE)
  182. {
  183. dwResult = ShowDialog();
  184. TBOOL(SwitchDesktop(hDeskInput));
  185. }
  186. }
  187. }
  188. (BOOL)CloseDesktop(hDeskInput);
  189. return(dwResult);
  190. }
  191. // --------------------------------------------------------------------------
  192. // CPowerButton::ShowDialog
  193. //
  194. // Arguments: <none>
  195. //
  196. // Returns: DWORD
  197. //
  198. // Purpose: Handles showing the dialog. This is called when the input
  199. // desktop is already winlogon's desktop or the desktop got
  200. // switched to winlogon's desktop. This should never be used on
  201. // WinSta0\Default in winlogon's process context.
  202. //
  203. // History: 2001-02-14 vtan created
  204. // --------------------------------------------------------------------------
  205. DWORD CPowerButton::ShowDialog (void)
  206. {
  207. DWORD dwResult;
  208. bool fCorrectContext;
  209. dwResult = MSGINA_DLG_FAILURE;
  210. if (_hToken != NULL)
  211. {
  212. fCorrectContext = (ImpersonateLoggedOnUser(_hToken) != FALSE);
  213. }
  214. else
  215. {
  216. fCorrectContext = true;
  217. }
  218. if (fCorrectContext)
  219. {
  220. TBOOL(_Gina_SetTimeout(_pWlxContext, LOGON_TIMEOUT));
  221. // In friendly UI bring up a Win32 dialog thru winlogon which
  222. // will get SAS and timeout events. Use this dialog to control
  223. // the lifetime of the friendly Turn Off Computer dialog.
  224. if (CSystemSettings::IsFriendlyUIActive())
  225. {
  226. dwResult = static_cast<DWORD>(_Gina_DialogBoxParam(_pWlxContext,
  227. _hDllInstance,
  228. MAKEINTRESOURCE(IDD_GINA_TURNOFFCOMPUTER),
  229. NULL,
  230. DialogProc,
  231. reinterpret_cast<LPARAM>(this)));
  232. }
  233. // In classic UI just bring up the classic UI dialog.
  234. // Ensure that invalid options are not allowed in the
  235. // combobox selections. This depends on whether a user
  236. // is logged onto the window station or not.
  237. else
  238. {
  239. DWORD dwExcludeOptions;
  240. HWND hwndParent;
  241. CDimmedWindow *pDimmedWindow;
  242. pDimmedWindow = new CDimmedWindow(_hDllInstance);
  243. if (pDimmedWindow != NULL)
  244. {
  245. hwndParent = pDimmedWindow->Create();
  246. }
  247. else
  248. {
  249. hwndParent = NULL;
  250. }
  251. if (_hToken != NULL)
  252. {
  253. dwExcludeOptions = SHTDN_RESTART_DOS | SHTDN_SLEEP2;
  254. }
  255. else
  256. {
  257. dwExcludeOptions = SHTDN_LOGOFF | SHTDN_RESTART_DOS | SHTDN_SLEEP2 | SHTDN_DISCONNECT;
  258. }
  259. dwResult = static_cast<DWORD>(_Gina_ShutdownDialog(_pWlxContext, hwndParent, dwExcludeOptions));
  260. if (pDimmedWindow != NULL)
  261. {
  262. pDimmedWindow->Release();
  263. }
  264. }
  265. TBOOL(_Gina_SetTimeout(_pWlxContext, 0));
  266. }
  267. if (fCorrectContext && (_hToken != NULL))
  268. {
  269. TBOOL(RevertToSelf());
  270. }
  271. return(dwResult);
  272. }
  273. // --------------------------------------------------------------------------
  274. // CPowerButton::DialogProc
  275. //
  276. // Arguments: See the platform SDK under DialogProc.
  277. //
  278. // Returns: INT_PTR
  279. //
  280. // Purpose: Handles dialog messages from the dialog manager. In particular
  281. // this traps SAS messages from winlogon.
  282. //
  283. // History: 2000-06-06 vtan created
  284. // --------------------------------------------------------------------------
  285. INT_PTR CALLBACK CPowerButton::DialogProc (HWND hwndDialog, UINT uMsg, WPARAM wParam, LPARAM lParam)
  286. {
  287. INT_PTR iResult;
  288. CPowerButton *pThis;
  289. pThis = reinterpret_cast<CPowerButton*>(GetWindowLongPtr(hwndDialog, GWLP_USERDATA));
  290. switch (uMsg)
  291. {
  292. case WM_INITDIALOG:
  293. {
  294. (LONG_PTR)SetWindowLongPtr(hwndDialog, GWLP_USERDATA, lParam);
  295. TBOOL(SetWindowPos(hwndDialog, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER));
  296. TBOOL(PostMessage(hwndDialog, WM_HIDEOURSELVES, 0, 0));
  297. iResult = TRUE;
  298. break;
  299. }
  300. case WM_HIDEOURSELVES:
  301. {
  302. (BOOL)ShowWindow(hwndDialog, SW_HIDE);
  303. TBOOL(PostMessage(hwndDialog, WM_READY, 0, 0));
  304. iResult = TRUE;
  305. break;
  306. }
  307. case WM_READY:
  308. {
  309. pThis->Handle_WM_READY(hwndDialog);
  310. iResult = TRUE;
  311. break;
  312. }
  313. case WLX_WM_SAS:
  314. {
  315. // Blow off CONTROL-ALT-DELETE presses.
  316. if (wParam == WLX_SAS_TYPE_CTRL_ALT_DEL)
  317. {
  318. iResult = TRUE;
  319. }
  320. else
  321. {
  322. // This dialog gets a WM_NULL from the Win32 dialog manager
  323. // when the dialog is ended from a timeout. This is input
  324. // timeout and not a screen saver timeout. Screen saver
  325. // timeouts will cause a WLX_SAS_TYPE_SCRNSVR_TIMEOUT to
  326. // be generated which is handled by RootDlgProc in winlogon.
  327. // The input timeout should be treated the same as the screen
  328. // saver timeout and cause the Turn Off dialog to go away.
  329. case WM_NULL:
  330. if (pThis->_pTurnOffDialog != NULL)
  331. {
  332. pThis->_pTurnOffDialog->Destroy();
  333. }
  334. pThis->_fCleanCompletion = false;
  335. iResult = FALSE;
  336. }
  337. break;
  338. }
  339. default:
  340. {
  341. iResult = FALSE;
  342. break;
  343. }
  344. }
  345. return(iResult);
  346. }
  347. // --------------------------------------------------------------------------
  348. // CPowerButton::Handle_WM_READY
  349. //
  350. // Arguments: hwndDialog = HWND of the hosting dialog.
  351. //
  352. // Returns: <none>
  353. //
  354. // Purpose: Handles showing the Turn Off Computer dialog hosted under
  355. // another dialog to trap SAS messages. Only change the returned
  356. // code via user32!EndDialog if the dialog was ended normally.
  357. // In abnormal circumstances winlogon has ended the dialog for
  358. // us with a specific code (e.g. screen saver timeout).
  359. //
  360. // History: 2000-06-06 vtan created
  361. // --------------------------------------------------------------------------
  362. INT_PTR CPowerButton::Handle_WM_READY (HWND hwndDialog)
  363. {
  364. INT_PTR iResult;
  365. iResult = SHTDN_NONE;
  366. _pTurnOffDialog = new CTurnOffDialog(_hDllInstance);
  367. if (_pTurnOffDialog != NULL)
  368. {
  369. iResult = _pTurnOffDialog->Show(NULL);
  370. delete _pTurnOffDialog;
  371. _pTurnOffDialog = NULL;
  372. if (_fCleanCompletion)
  373. {
  374. TBOOL(EndDialog(hwndDialog, CTurnOffDialog::ShellCodeToGinaCode(static_cast<DWORD>(iResult))));
  375. }
  376. }
  377. return(iResult);
  378. }
  379. // --------------------------------------------------------------------------
  380. // CPowerButtonExecution::CPowerButtonExecution
  381. //
  382. // Arguments: dwShutdownRequest = SHTDN_xxx request.
  383. //
  384. // Returns: <none>
  385. //
  386. // Purpose: Constructor for the CPowerButtonExecution class. Invokes the
  387. // appropriate shutdown request on a different thread so the
  388. // SASWndProc thread is NOT blocked.
  389. //
  390. // History: 2000-04-18 vtan created
  391. // --------------------------------------------------------------------------
  392. CPowerButtonExecution::CPowerButtonExecution (DWORD dwShutdownRequest) :
  393. CThread(),
  394. _dwShutdownRequest(dwShutdownRequest),
  395. _hToken(NULL)
  396. {
  397. (BOOL)OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, FALSE, &_hToken);
  398. Resume();
  399. }
  400. // --------------------------------------------------------------------------
  401. // CPowerButtonExecution::~CPowerButtonExecution
  402. //
  403. // Arguments: <none>
  404. //
  405. // Returns: <none>
  406. //
  407. // Purpose: Destructor for the CPowerButtonExecution class. Releases
  408. // resources used by the class.
  409. //
  410. // History: 2000-04-18 vtan created
  411. // --------------------------------------------------------------------------
  412. CPowerButtonExecution::~CPowerButtonExecution (void)
  413. {
  414. ReleaseHandle(_hToken);
  415. }
  416. // --------------------------------------------------------------------------
  417. // CPowerButtonExecution::Entry
  418. //
  419. // Arguments: <none>
  420. //
  421. // Returns: DWORD
  422. //
  423. // Purpose: Main entry function. This performs the request and exits the
  424. // thread.
  425. //
  426. // History: 2000-04-18 vtan created
  427. // --------------------------------------------------------------------------
  428. DWORD CPowerButtonExecution::Entry (void)
  429. {
  430. bool fCorrectContext;
  431. if (_hToken != NULL)
  432. {
  433. fCorrectContext = (ImpersonateLoggedOnUser(_hToken) != FALSE);
  434. }
  435. else
  436. {
  437. fCorrectContext = true;
  438. }
  439. if (fCorrectContext)
  440. {
  441. CPrivilegeEnable privilege(SE_SHUTDOWN_NAME);
  442. switch (_dwShutdownRequest & ~MSGINA_DLG_FLAG_MASK)
  443. {
  444. case MSGINA_DLG_USER_LOGOFF:
  445. case MSGINA_DLG_SHUTDOWN:
  446. {
  447. DWORD dwRequestFlags;
  448. dwRequestFlags = _dwShutdownRequest & MSGINA_DLG_FLAG_MASK;
  449. switch (dwRequestFlags)
  450. {
  451. case 0:
  452. case MSGINA_DLG_SHUTDOWN_FLAG:
  453. case MSGINA_DLG_REBOOT_FLAG:
  454. case MSGINA_DLG_POWEROFF_FLAG:
  455. {
  456. UINT uiFlags;
  457. if (dwRequestFlags == 0)
  458. {
  459. uiFlags = EWX_LOGOFF;
  460. }
  461. else if (dwRequestFlags == MSGINA_DLG_REBOOT_FLAG)
  462. {
  463. uiFlags = EWX_WINLOGON_OLD_REBOOT;
  464. }
  465. else
  466. {
  467. SYSTEM_POWER_CAPABILITIES spc;
  468. (NTSTATUS)NtPowerInformation(SystemPowerCapabilities,
  469. NULL,
  470. 0,
  471. &spc,
  472. sizeof(spc));
  473. if (spc.SystemS4)
  474. {
  475. uiFlags = EWX_WINLOGON_OLD_POWEROFF;
  476. }
  477. else
  478. {
  479. uiFlags = EWX_WINLOGON_OLD_SHUTDOWN;
  480. }
  481. }
  482. TBOOL(ExitWindowsEx(uiFlags, 0));
  483. break;
  484. }
  485. case MSGINA_DLG_SLEEP_FLAG:
  486. case MSGINA_DLG_SLEEP2_FLAG:
  487. case MSGINA_DLG_HIBERNATE_FLAG:
  488. {
  489. POWER_ACTION pa;
  490. if (dwRequestFlags == MSGINA_DLG_HIBERNATE_FLAG)
  491. {
  492. pa = PowerActionHibernate;
  493. }
  494. else
  495. {
  496. pa = PowerActionSleep;
  497. }
  498. (NTSTATUS)NtInitiatePowerAction(pa,
  499. PowerSystemSleeping1,
  500. POWER_ACTION_QUERY_ALLOWED | POWER_ACTION_UI_ALLOWED,
  501. FALSE);
  502. break;
  503. }
  504. default:
  505. {
  506. WARNINGMSG("Unknown MSGINA_DLG_xxx_FLAG used in CPowerButtonExecution::Entry");
  507. break;
  508. }
  509. }
  510. break;
  511. }
  512. case MSGINA_DLG_DISCONNECT:
  513. {
  514. (BOOLEAN)WinStationDisconnect(SERVERNAME_CURRENT, LOGONID_CURRENT, FALSE);
  515. break;
  516. }
  517. default:
  518. {
  519. WARNINGMSG("Unknown MSGINA_DLG_xxx_ used in CPowerButtonExecution::Entry");
  520. break;
  521. }
  522. }
  523. }
  524. if (fCorrectContext && (_hToken != NULL))
  525. {
  526. TBOOL(RevertToSelf());
  527. }
  528. return(0);
  529. }