|
|
// --------------------------------------------------------------------------
// Module Name: PowerButton.cpp
//
// Copyright (c) 2000, Microsoft Corporation
//
// Implementation file for CPowerButton class which handles the ACPI power
// button.
//
// History: 2000-04-17 vtan created
// --------------------------------------------------------------------------
#include "StandardHeader.h"
#include "PowerButton.h"
#include <msginaexports.h>
#include <shlobj.h>
#include <shlobjp.h>
#include <shellapi.h>
#include <shlapip.h>
#include <winsta.h>
#include <ginarcid.h>
#include "DimmedWindow.h"
#include "Impersonation.h"
#include "PrivilegeEnable.h"
#include "SystemSettings.h"
#define WM_HIDEOURSELVES (WM_USER + 10000)
#define WM_READY (WM_USER + 10001)
// --------------------------------------------------------------------------
// CPowerButton::CPowerButton
//
// Arguments: pWlxContext = PGLOBALS allocated at WlxInitialize.
// hDllInstance = HINSTANCE of the hosting DLL or EXE.
//
// Returns: <none>
//
// Purpose: Constructor for the CPowerButton class. It opens the effective
// token of the caller (which is actually impersonating the
// current user) for assignment in its thread token when
// execution begins. The token cannot be assigned now because
// the current thread is impersonating the user context and it
// cannot assign the token to the newly created thread running in
// the SYSTEM context.
//
// History: 2000-04-18 vtan created
// --------------------------------------------------------------------------
CPowerButton::CPowerButton (void *pWlxContext, HINSTANCE hDllInstance) : CThread(), _pWlxContext(pWlxContext), _hDllInstance(hDllInstance), _hToken(NULL), _pTurnOffDialog(NULL), _fCleanCompletion(true)
{ (BOOL)OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, FALSE, &_hToken); Resume(); }
// --------------------------------------------------------------------------
// CPowerButton::~CPowerButton
//
// Arguments: <none>
//
// Returns: <none>
//
// Purpose: Destructor for the CPowerButton class. Cleans up resources
// used by the class.
//
// History: 2000-04-18 vtan created
// --------------------------------------------------------------------------
CPowerButton::~CPowerButton (void)
{ ASSERTMSG(_pTurnOffDialog == NULL, "_pTurnOffDialog is not NULL in CPowerButton::~CPowerButton"); ReleaseHandle(_hToken); }
// --------------------------------------------------------------------------
// CPowerButton::IsValidExecutionCode
//
// Arguments: dwGinaCode
//
// Returns: bool
//
// Purpose: Returns whether the given MSGINA_DLG_xxx code is valid. It
// does fully verify the validity of the MSGINA_DLG_xxx_FLAG
// options.
//
// History: 2000-06-06 vtan created
// --------------------------------------------------------------------------
bool CPowerButton::IsValidExecutionCode (DWORD dwGinaCode)
{ DWORD dwExecutionCode;
dwExecutionCode = dwGinaCode & ~MSGINA_DLG_FLAG_MASK; return((dwExecutionCode == MSGINA_DLG_USER_LOGOFF) || (dwExecutionCode == MSGINA_DLG_SHUTDOWN) || (dwExecutionCode == MSGINA_DLG_DISCONNECT)); }
// --------------------------------------------------------------------------
// CPowerButton::Entry
//
// Arguments: <none>
//
// Returns: DWORD
//
// Purpose: Main function of the thread. Change the thread's desktop first
// in case the actual input desktop is Winlogon's which is the
// secure desktop. Then change the thread's token so that the
// user's privileges are respected in the action choices. This
// actually isn't critical because the physical button on the
// keyboard is pressed which means they can physically remove the
// power also!
//
// History: 2000-04-18 vtan created
// --------------------------------------------------------------------------
DWORD CPowerButton::Entry (void)
{ DWORD dwResult; HDESK hDeskInput; CDesktop desktop;
dwResult = MSGINA_DLG_FAILURE;
// Get the input desktop.
hDeskInput = OpenInputDesktop(0, FALSE, MAXIMUM_ALLOWED); if (hDeskInput != NULL) { bool fHandled; DWORD dwLengthNeeded; TCHAR szDesktopName[256];
fHandled = false;
// Get the desktop's name.
if (GetUserObjectInformation(hDeskInput, UOI_NAME, szDesktopName, sizeof(szDesktopName), &dwLengthNeeded) != FALSE) {
// If the desktop is "Winlogon" (case insensitive) then
// assume that the secure desktop is showing. It's safe
// to display the dialog and handle it inline.
if (lstrcmpi(szDesktopName, TEXT("winlogon")) == 0) { dwResult = ShowDialog(); fHandled = true; } else { CDesktop desktopTemp;
// The input desktop is something else. Check the name.
// If it's "Default" (case insensitive) then assume that
// explorer is going to handle this message. Go find explorer's
// tray window. Check it's not hung by probing with a
// SendMessageTimeout. If that shows it's not hung then
// send it the real message. If it's hung then don't let
// explorer process this message. Instead handle it
// internally with the funky desktop switch stuff.
if (NT_SUCCESS(desktopTemp.SetInput())) { HWND hwnd;
hwnd = FindWindow(TEXT("Shell_TrayWnd"), NULL); if (hwnd != NULL) { DWORD dwProcessID;
DWORD_PTR dwUnused;
(DWORD)GetWindowThreadProcessId(hwnd, &dwProcessID); if (SendMessageTimeout(hwnd, WM_NULL, 0, 0, SMTO_NORMAL, 500, &dwUnused) != 0) {
// Before asking explorer to bring up the dialog
// allow it to set the foreground window. We have
// this power because win32k gave it to us when
// the ACPI power button message was sent to winlogon.
(BOOL)AllowSetForegroundWindow(dwProcessID); (LRESULT)SendMessage(hwnd, WM_CLOSE, 0, 0); fHandled = true; } } } } }
// If the request couldn't be handled then switch the desktop to
// winlogon's desktop and handle it here. This secures the dialog
// on the secure desktop from rogue processes sending bogus messages
// and crashing processes. The input desktop is required to be
// switched. If this fails there's little that can be done. Ignore
// this gracefully.
if (!fHandled) { if (SwitchDesktop(GetThreadDesktop(GetCurrentThreadId())) != FALSE) { dwResult = ShowDialog(); TBOOL(SwitchDesktop(hDeskInput)); } } } (BOOL)CloseDesktop(hDeskInput); return(dwResult); }
// --------------------------------------------------------------------------
// CPowerButton::ShowDialog
//
// Arguments: <none>
//
// Returns: DWORD
//
// Purpose: Handles showing the dialog. This is called when the input
// desktop is already winlogon's desktop or the desktop got
// switched to winlogon's desktop. This should never be used on
// WinSta0\Default in winlogon's process context.
//
// History: 2001-02-14 vtan created
// --------------------------------------------------------------------------
DWORD CPowerButton::ShowDialog (void)
{ DWORD dwResult; bool fCorrectContext;
dwResult = MSGINA_DLG_FAILURE; if (_hToken != NULL) { fCorrectContext = (ImpersonateLoggedOnUser(_hToken) != FALSE); } else { fCorrectContext = true; } if (fCorrectContext) { TBOOL(_Gina_SetTimeout(_pWlxContext, LOGON_TIMEOUT));
// In friendly UI bring up a Win32 dialog thru winlogon which
// will get SAS and timeout events. Use this dialog to control
// the lifetime of the friendly Turn Off Computer dialog.
if (CSystemSettings::IsFriendlyUIActive()) { dwResult = static_cast<DWORD>(_Gina_DialogBoxParam(_pWlxContext, _hDllInstance, MAKEINTRESOURCE(IDD_GINA_TURNOFFCOMPUTER), NULL, DialogProc, reinterpret_cast<LPARAM>(this))); }
// In classic UI just bring up the classic UI dialog.
// Ensure that invalid options are not allowed in the
// combobox selections. This depends on whether a user
// is logged onto the window station or not.
else { DWORD dwExcludeOptions; HWND hwndParent; CDimmedWindow *pDimmedWindow;
pDimmedWindow = new CDimmedWindow(_hDllInstance); if (pDimmedWindow != NULL) { hwndParent = pDimmedWindow->Create(); } else { hwndParent = NULL; } if (_hToken != NULL) { dwExcludeOptions = SHTDN_RESTART_DOS | SHTDN_SLEEP2; } else { dwExcludeOptions = SHTDN_LOGOFF | SHTDN_RESTART_DOS | SHTDN_SLEEP2 | SHTDN_DISCONNECT; } dwResult = static_cast<DWORD>(_Gina_ShutdownDialog(_pWlxContext, hwndParent, dwExcludeOptions)); if (pDimmedWindow != NULL) { pDimmedWindow->Release(); } } TBOOL(_Gina_SetTimeout(_pWlxContext, 0)); } if (fCorrectContext && (_hToken != NULL)) { TBOOL(RevertToSelf()); } return(dwResult); }
// --------------------------------------------------------------------------
// CPowerButton::DialogProc
//
// Arguments: See the platform SDK under DialogProc.
//
// Returns: INT_PTR
//
// Purpose: Handles dialog messages from the dialog manager. In particular
// this traps SAS messages from winlogon.
//
// History: 2000-06-06 vtan created
// --------------------------------------------------------------------------
INT_PTR CALLBACK CPowerButton::DialogProc (HWND hwndDialog, UINT uMsg, WPARAM wParam, LPARAM lParam)
{ INT_PTR iResult; CPowerButton *pThis;
pThis = reinterpret_cast<CPowerButton*>(GetWindowLongPtr(hwndDialog, GWLP_USERDATA)); switch (uMsg) { case WM_INITDIALOG: { (LONG_PTR)SetWindowLongPtr(hwndDialog, GWLP_USERDATA, lParam); TBOOL(SetWindowPos(hwndDialog, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER)); TBOOL(PostMessage(hwndDialog, WM_HIDEOURSELVES, 0, 0)); iResult = TRUE; break; } case WM_HIDEOURSELVES: { (BOOL)ShowWindow(hwndDialog, SW_HIDE); TBOOL(PostMessage(hwndDialog, WM_READY, 0, 0)); iResult = TRUE; break; } case WM_READY: { pThis->Handle_WM_READY(hwndDialog); iResult = TRUE; break; } case WLX_WM_SAS: {
// Blow off CONTROL-ALT-DELETE presses.
if (wParam == WLX_SAS_TYPE_CTRL_ALT_DEL) { iResult = TRUE; } else {
// This dialog gets a WM_NULL from the Win32 dialog manager
// when the dialog is ended from a timeout. This is input
// timeout and not a screen saver timeout. Screen saver
// timeouts will cause a WLX_SAS_TYPE_SCRNSVR_TIMEOUT to
// be generated which is handled by RootDlgProc in winlogon.
// The input timeout should be treated the same as the screen
// saver timeout and cause the Turn Off dialog to go away.
case WM_NULL: if (pThis->_pTurnOffDialog != NULL) { pThis->_pTurnOffDialog->Destroy(); } pThis->_fCleanCompletion = false; iResult = FALSE; } break; } default: { iResult = FALSE; break; } } return(iResult); }
// --------------------------------------------------------------------------
// CPowerButton::Handle_WM_READY
//
// Arguments: hwndDialog = HWND of the hosting dialog.
//
// Returns: <none>
//
// Purpose: Handles showing the Turn Off Computer dialog hosted under
// another dialog to trap SAS messages. Only change the returned
// code via user32!EndDialog if the dialog was ended normally.
// In abnormal circumstances winlogon has ended the dialog for
// us with a specific code (e.g. screen saver timeout).
//
// History: 2000-06-06 vtan created
// --------------------------------------------------------------------------
INT_PTR CPowerButton::Handle_WM_READY (HWND hwndDialog)
{ INT_PTR iResult;
iResult = SHTDN_NONE; _pTurnOffDialog = new CTurnOffDialog(_hDllInstance); if (_pTurnOffDialog != NULL) { iResult = _pTurnOffDialog->Show(NULL); delete _pTurnOffDialog; _pTurnOffDialog = NULL; if (_fCleanCompletion) { TBOOL(EndDialog(hwndDialog, CTurnOffDialog::ShellCodeToGinaCode(static_cast<DWORD>(iResult)))); } } return(iResult); }
// --------------------------------------------------------------------------
// CPowerButtonExecution::CPowerButtonExecution
//
// Arguments: dwShutdownRequest = SHTDN_xxx request.
//
// Returns: <none>
//
// Purpose: Constructor for the CPowerButtonExecution class. Invokes the
// appropriate shutdown request on a different thread so the
// SASWndProc thread is NOT blocked.
//
// History: 2000-04-18 vtan created
// --------------------------------------------------------------------------
CPowerButtonExecution::CPowerButtonExecution (DWORD dwShutdownRequest) : CThread(), _dwShutdownRequest(dwShutdownRequest), _hToken(NULL)
{ (BOOL)OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, FALSE, &_hToken); Resume(); }
// --------------------------------------------------------------------------
// CPowerButtonExecution::~CPowerButtonExecution
//
// Arguments: <none>
//
// Returns: <none>
//
// Purpose: Destructor for the CPowerButtonExecution class. Releases
// resources used by the class.
//
// History: 2000-04-18 vtan created
// --------------------------------------------------------------------------
CPowerButtonExecution::~CPowerButtonExecution (void)
{ ReleaseHandle(_hToken); }
// --------------------------------------------------------------------------
// CPowerButtonExecution::Entry
//
// Arguments: <none>
//
// Returns: DWORD
//
// Purpose: Main entry function. This performs the request and exits the
// thread.
//
// History: 2000-04-18 vtan created
// --------------------------------------------------------------------------
DWORD CPowerButtonExecution::Entry (void)
{ bool fCorrectContext;
if (_hToken != NULL) { fCorrectContext = (ImpersonateLoggedOnUser(_hToken) != FALSE); } else { fCorrectContext = true; } if (fCorrectContext) { CPrivilegeEnable privilege(SE_SHUTDOWN_NAME);
switch (_dwShutdownRequest & ~MSGINA_DLG_FLAG_MASK) { case MSGINA_DLG_USER_LOGOFF: case MSGINA_DLG_SHUTDOWN: { DWORD dwRequestFlags;
dwRequestFlags = _dwShutdownRequest & MSGINA_DLG_FLAG_MASK; switch (dwRequestFlags) { case 0: case MSGINA_DLG_SHUTDOWN_FLAG: case MSGINA_DLG_REBOOT_FLAG: case MSGINA_DLG_POWEROFF_FLAG: { UINT uiFlags;
if (dwRequestFlags == 0) { uiFlags = EWX_LOGOFF; } else if (dwRequestFlags == MSGINA_DLG_REBOOT_FLAG) { uiFlags = EWX_WINLOGON_OLD_REBOOT; } else { SYSTEM_POWER_CAPABILITIES spc;
(NTSTATUS)NtPowerInformation(SystemPowerCapabilities, NULL, 0, &spc, sizeof(spc)); if (spc.SystemS4) { uiFlags = EWX_WINLOGON_OLD_POWEROFF; } else { uiFlags = EWX_WINLOGON_OLD_SHUTDOWN; } } TBOOL(ExitWindowsEx(uiFlags, 0)); break; } case MSGINA_DLG_SLEEP_FLAG: case MSGINA_DLG_SLEEP2_FLAG: case MSGINA_DLG_HIBERNATE_FLAG: { POWER_ACTION pa;
if (dwRequestFlags == MSGINA_DLG_HIBERNATE_FLAG) { pa = PowerActionHibernate; } else { pa = PowerActionSleep; } (NTSTATUS)NtInitiatePowerAction(pa, PowerSystemSleeping1, POWER_ACTION_QUERY_ALLOWED | POWER_ACTION_UI_ALLOWED, FALSE); break; } default: { WARNINGMSG("Unknown MSGINA_DLG_xxx_FLAG used in CPowerButtonExecution::Entry"); break; } } break; } case MSGINA_DLG_DISCONNECT: { (BOOLEAN)WinStationDisconnect(SERVERNAME_CURRENT, LOGONID_CURRENT, FALSE); break; } default: { WARNINGMSG("Unknown MSGINA_DLG_xxx_ used in CPowerButtonExecution::Entry"); break; } } } if (fCorrectContext && (_hToken != NULL)) { TBOOL(RevertToSelf()); } return(0); }
|