/****************************** Module Header ******************************\
* Module Name: sas.c
*
* Copyright (c) 1991, Microsoft Corporation
*
* Support routines to implement processing of the secure attention sequence
*
* Users must always press the SAS key sequence before entering a password.
* This module catches the key press and forwards a SAS message to the
* correct winlogon window.
*
* History:
* 12-05-91 Davidc Created.
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
// Internal Prototypes
LONG SASWndProc(
HWND hwnd,
UINT message,
DWORD wParam,
LONG lParam);
BOOL SASCreate(
HWND hwnd);
BOOL SASDestroy(
HWND hwnd);
// Global used to hold the window handle of the SAS window.
static HWND hwndSAS = NULL;
// LATER this hwndSAS will have to go in instance data when we have multiple threads
// Global for SAS window class name
static PWCHAR szSASClass = TEXT("SAS window class");
#if DBG
#define DEFAULT_QUICK_REBOOT 1
#else
#define DEFAULT_QUICK_REBOOT 0
#endif
#define SHELL_RESTART_TIMER_ID 100
/***************************************************************************\
* SASInit
*
* Initialises this module.
*
* Creates a window to receive the SAS and registers the
* key sequence as a hot key.
*
* Returns TRUE on success, FALSE on failure.
*
* 12-05-91 Davidc Created.
\***************************************************************************/
BOOL SASInit(
PGLOBALS pGlobals)
{
WNDCLASS wc;
if (hwndSAS != NULL) {
DebugLog((DEB_ERROR, "SAS module already initialized !!"));
return(FALSE);
}
//
// Register the notification window class
//
wc.style = CS_SAVEBITS;
wc.lpfnWndProc = (WNDPROC)SASWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = pGlobals->hInstance;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = szSASClass;
if (!RegisterClass(&wc))
return FALSE;
hwndSAS = CreateWindowEx(0L, szSASClass, TEXT("SAS window"),
WS_OVERLAPPEDWINDOW,
0, 0, 0, 0,
NULL, NULL, pGlobals->hInstance, NULL);
if (hwndSAS == NULL)
return FALSE;
//
// Store our globals pointer in the window user data
//
SetWindowLong(hwndSAS, GWL_USERDATA, (LONG)pGlobals);
//
// Register this window with windows so we get notified for
// screen-saver startup and user log-off
//
if (!SetLogonNotifyWindow(pGlobals->WindowStation.hwinsta, hwndSAS)) {
DebugLog((DEB_ERROR, "Failed to set logon notify window"));
return(FALSE);
}
return(TRUE);
}
/***************************************************************************\
* SASTerminate
*
* Terminates this module.
*
* Unregisters the SAS and destroys the SAS windows
*
* 12-05-91 Davidc Created.
\***************************************************************************/
VOID SASTerminate(VOID)
{
DestroyWindow(hwndSAS);
// Reset our globals
hwndSAS = NULL;
}
/***************************************************************************\
* SASWndProc
*
* Window procedure for the SAS window.
*
* This window registers the SAS hotkey sequence, and forwards any hotkey
* messages to the current winlogon window. It does this using a
* timeout module function. i.e. every window should register a timeout
* even if it's 0 if they want to get SAS messages.
*
* History:
* 12-09-91 Davidc Created.
\***************************************************************************/
LONG SASWndProc(
HWND hwnd,
UINT message,
DWORD wParam,
LONG lParam)
{
PGLOBALS pGlobals = (PGLOBALS)GetWindowLong(hwnd, GWL_USERDATA);
switch (message)
{
case WM_CREATE:
if (!SASCreate(hwnd))
{
return(TRUE); // Fail creation
}
return(FALSE); // Continue creating window
case WM_DESTROY:
DebugLog(( DEB_TRACE, "SAS Window Shutting down?\n"));
#if DBG
DebugBreak();
#endif
SASDestroy(hwnd);
return(0);
case WM_HOTKEY:
if (wParam == 1)
{
QuickReboot(pGlobals, TRUE);
return(0);
}
#if DBG
if (wParam == 2)
{
switch (pGlobals->WindowStation.ActiveDesktop)
{
case Desktop_Winlogon:
SetActiveDesktop(&pGlobals->WindowStation, Desktop_Application);
break;
case Desktop_Application:
SetActiveDesktop(&pGlobals->WindowStation, Desktop_Winlogon);
break;
}
return(0);
}
if (wParam == 3)
{
DebugBreak();
return(0);
}
#endif
if (wParam == 4)
{
PGINASESSION pGina = pGlobals->pGina;
WCHAR szTaskMgr[] = L"taskmgr.exe";
DebugLog((DEB_TRACE, "Starting taskmgr.exe.\n"));
if (pGlobals->UserLoggedOn ) {
pGina->pWlxStartApplication(pGina->pGinaContext,
APPLICATION_DESKTOP_PATH,
pGlobals->UserProcessData.pEnvironment,
szTaskMgr);
}
return(0);
}
CADNotify(pGlobals, WLX_SAS_TYPE_CTRL_ALT_DEL);
return(0);
case WM_LOGONNOTIFY: // A private notification from Windows
DebugLog((DEB_TRACE_SAS, "LOGONNOTIFY message %d\n", wParam ));
switch (wParam)
{
case LOGON_LOGOFF:
#if DBG
DebugLog((DEB_TRACE_SAS, "\tWINLOGON : %s\n", (lParam & EWX_WINLOGON_CALLER) ? "True" : "False"));
DebugLog((DEB_TRACE_SAS, "\tSYSTEM : %s\n", (lParam & EWX_SYSTEM_CALLER) ? "True" : "False"));
DebugLog((DEB_TRACE_SAS, "\tSHUTDOWN : %s\n", (lParam & EWX_SHUTDOWN) ? "True" : "False"));
DebugLog((DEB_TRACE_SAS, "\tREBOOT : %s\n", (lParam & EWX_REBOOT) ? "True" : "False"));
DebugLog((DEB_TRACE_SAS, "\tPOWEROFF : %s\n", (lParam & EWX_POWEROFF) ? "True" : "False"));
DebugLog((DEB_TRACE_SAS, "\tFORCE : %s\n", (lParam & EWX_FORCE) ? "True" : "False"));
DebugLog((DEB_TRACE_SAS, "\tOLD_SYSTEM : %s\n", (lParam & EWX_WINLOGON_OLD_SYSTEM) ? "True" : "False"));
DebugLog((DEB_TRACE_SAS, "\tOLD_SHUTDOWN : %s\n", (lParam & EWX_WINLOGON_OLD_SHUTDOWN) ? "True" : "False"));
DebugLog((DEB_TRACE_SAS, "\tOLD_REBOOT : %s\n", (lParam & EWX_WINLOGON_OLD_REBOOT) ? "True" : "False"));
DebugLog((DEB_TRACE_SAS, "\tOLD_POWEROFF : %s\n", (lParam & EWX_WINLOGON_OLD_POWEROFF) ? "True" : "False"));
#endif
//
// If there is an exit windows in progress, reject this
// message if it is not our own call coming back. This
// prevents people from calling ExitWindowsEx repeatedly
//
if ( ExitWindowsInProgress &&
( !( lParam & EWX_WINLOGON_CALLER ) ) )
{
break;
}
pGlobals->LogoffFlags = lParam;
CADNotify(pGlobals, WLX_SAS_TYPE_USER_LOGOFF);
break;
case LOGON_INPUT_TIMEOUT:
//
// Notify the current window
//
// ForwardMessage(pGlobals, WM_SCREEN_SAVER_TIMEOUT, 0, 0);
CADNotify(pGlobals, WLX_SAS_TYPE_SCRNSVR_TIMEOUT);
break;
case LOGON_RESTARTSHELL:
//
// Restart the shell after X seconds
//
// We don't restart the shell for the following conditions:
//
// 1) No one is logged on
// 2) We are in the process of logging off
// (logoffflags will be non-zero)
// 3) The shell exiting gracefully
// (Exit status is in lParam. 1 = graceful)
// 4) A new user has logged on after the request
// to restart the shell.
// (in the case of autoadminlogon, the new
// user could be logged on before the restart
// request comes through).
//
if (!pGlobals->UserLoggedOn ||
pGlobals->LogoffFlags ||
(lParam == 1) ||
(pGlobals->TickCount > (DWORD)GetMessageTime())) {
break;
}
SetTimer (hwnd, SHELL_RESTART_TIMER_ID, 2000, NULL);
break;
}
return(0);
case WM_TIMER:
{
PGINASESSION pGina;
LONG lResult;
HKEY hKey;
BOOL bRestart = TRUE;
DWORD dwType, dwSize;
//
// Restart the shell
//
if (wParam != SHELL_RESTART_TIMER_ID) {
break;
}
KillTimer (hwnd, SHELL_RESTART_TIMER_ID);
//
// Check if we should restart the shell
//
lResult = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
WINLOGON_KEY,
0,
KEY_READ,
&hKey);
if (lResult == ERROR_SUCCESS) {
dwSize = sizeof(bRestart);
RegQueryValueEx (hKey,
TEXT("AutoRestartShell"),
NULL,
&dwType,
(LPBYTE) &bRestart,
&dwSize);
RegCloseKey (hKey);
}
if (bRestart) {
PWCH pchData;
PWSTR pszTok;
DebugLog((DEB_TRACE, "Restarting user's shell.\n"));
pGina = pGlobals->pGina;
pchData = AllocAndGetPrivateProfileString(APPLICATION_NAME,
SHELL_KEY,
TEXT("explorer.exe"),
NULL);
if (!pchData) {
break;
}
pszTok = wcstok(pchData, TEXT(","));
while (pszTok)
{
if (*pszTok == TEXT(' '))
{
while (*pszTok++ == TEXT(' '))
;
}
if (pGina->pWlxStartApplication(pGina->pGinaContext,
APPLICATION_DESKTOP_PATH,
pGlobals->UserProcessData.pEnvironment,
pszTok)) {
ReportWinlogonEvent(pGlobals,
EVENTLOG_INFORMATION_TYPE,
EVENT_SHELL_RESTARTED,
0,
NULL,
1,
pszTok);
}
pszTok = wcstok(NULL, TEXT(","));
}
Free(pchData);
}
}
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0L;
}
BOOL bRegisteredQuickReboot;
BOOL bRegisteredDesktopSwitching;
BOOL bRegisteredWinlogonBreakpoint;
BOOL bRegisteredTaskmgr;
/***************************************************************************\
* SASCreate
*
* Does any processing required for WM_CREATE message.
*
* Returns TRUE on success, FALSE on failure
*
* History:
* 12-09-91 Davidc Created.
\***************************************************************************/
BOOL SASCreate(
HWND hwnd)
{
// Register the SAS unless we are told not to.
if (GetProfileInt( APPNAME_WINLOGON, VARNAME_AUTOLOGON, 0 ) != 2) {
if (!RegisterHotKey(hwnd, 0, MOD_CONTROL | MOD_ALT, VK_DELETE)) {
DebugLog((DEB_ERROR, "failed to register SAS"));
return(FALSE); // Fail creation
}
}
//
// (Ctrl+Alt+Shift+Del) hotkey to reboot into DOS directly
//
if (GetProfileInt( APPNAME_WINLOGON, VARNAME_ENABLEQUICKREBOOT, DEFAULT_QUICK_REBOOT) != 0) {
if (!RegisterHotKey(hwnd, 1, MOD_CONTROL | MOD_ALT | MOD_SHIFT, VK_DELETE)) {
DebugLog((DEB_ERROR, "failed to register quick reboot SAS"));
bRegisteredQuickReboot = FALSE;
} else {
bRegisteredQuickReboot = TRUE;
}
}
#if DBG
//
// (Ctrl+Alt+Tab) will switch between desktops
//
if (GetProfileInt( APPNAME_WINLOGON, VARNAME_ENABLEDESKTOPSWITCHING, 0 ) != 0) {
if (!RegisterHotKey(hwnd, 2, MOD_CONTROL | MOD_ALT, VK_TAB)) {
DebugLog((DEB_ERROR, "failed to register desktop switch SAS"));
bRegisteredDesktopSwitching = FALSE;
} else {
bRegisteredDesktopSwitching = TRUE;
}
}
if (WinlogonInfoLevel & DEB_COOL_SWITCH) {
if (!RegisterHotKey(hwnd, 3, MOD_CONTROL | MOD_ALT | MOD_SHIFT, VK_TAB)) {
DebugLog((DEB_ERROR, "failed to register breakpoint SAS"));
bRegisteredWinlogonBreakpoint = FALSE;
} else {
bRegisteredWinlogonBreakpoint = TRUE;
}
}
#endif
//
// (Ctrl+Shift+Esc) will start taskmgr
//
if (!RegisterHotKey(hwnd, 4, MOD_CONTROL | MOD_SHIFT, VK_ESCAPE)) {
DebugLog((DEB_ERROR, "failed to register taskmgr hotkey"));
bRegisteredTaskmgr = FALSE;
} else {
bRegisteredTaskmgr = TRUE;
}
return(TRUE);
}
/***************************************************************************\
* SASDestroy
*
* Does any processing required for WM_DESTROY message.
*
* Returns TRUE on success, FALSE on failure
*
* History:
* 12-09-91 Davidc Created.
\***************************************************************************/
BOOL SASDestroy(
HWND hwnd)
{
// Unregister the SAS
UnregisterHotKey(hwnd, 0);
if (bRegisteredQuickReboot) {
UnregisterHotKey(hwnd, 1);
}
if (bRegisteredDesktopSwitching) {
UnregisterHotKey(hwnd, 2);
}
#if DBG
if (bRegisteredWinlogonBreakpoint) {
UnregisterHotKey(hwnd, 3);
}
#endif
if (bRegisteredTaskmgr) {
UnregisterHotKey(hwnd, 4);
}
return(TRUE);
}