mirror of https://github.com/lianthony/NT4.0
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.
2126 lines
57 KiB
2126 lines
57 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: wlx.c
|
|
*
|
|
* Copyright (c) 1991, Microsoft Corporation
|
|
*
|
|
* Winlogon main module
|
|
*
|
|
* History:
|
|
* 12-09-91 Davidc Created.
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#define WM_HIDEOURSELVES WM_USER + 600
|
|
|
|
|
|
#if DBG
|
|
char * SASTypes[] = { "Timeout", "Ctrl-Alt-Del", "ScreenSaver Timeout",
|
|
"ScreenSaver Activity", "User Logoff" };
|
|
#define SASName(x) (x < (sizeof(SASTypes) / sizeof(char *)) ? SASTypes[x] : "User Defined")
|
|
|
|
char * WlxRets[] = { "invalid", "Logon", "None", "LockWksta", "Logoff", "Shutdown",
|
|
"Pwd Changed", "TaskList", "UnlockWksta", "ForceLogoff",
|
|
"Shutdown-PowerOff", "Shutdown-Reboot" };
|
|
#define WlxName(x) (x < (sizeof(WlxRets) / sizeof(char *)) ? WlxRets[x] : "Invalid")
|
|
#endif
|
|
|
|
#define IsShutdown(x) ((x == WLX_SAS_ACTION_SHUTDOWN) || \
|
|
(x == WLX_SAS_ACTION_SHUTDOWN_REBOOT) || \
|
|
(x == WLX_SAS_ACTION_SHUTDOWN_POWER_OFF) )
|
|
|
|
#define RealFlagsFromStoredFlags(Flags) \
|
|
EWX_LOGOFF | \
|
|
((Flags & EWX_WINLOGON_OLD_SYSTEM) ? EWX_SYSTEM_CALLER : 0) | \
|
|
((Flags & EWX_WINLOGON_OLD_SHUTDOWN) ? EWX_SHUTDOWN : 0) | \
|
|
((Flags & EWX_WINLOGON_OLD_REBOOT) ? EWX_REBOOT : 0) | \
|
|
((Flags & EWX_WINLOGON_OLD_POWEROFF) ? EWX_POWEROFF : 0)
|
|
|
|
#define StoredFlagsFromRealFlags(Flags) \
|
|
EWX_LOGOFF | \
|
|
((Flags & EWX_SYSTEM_CALLER) ? EWX_WINLOGON_OLD_SYSTEM : 0) | \
|
|
((Flags & EWX_SHUTDOWN) ? EWX_WINLOGON_OLD_SHUTDOWN : 0) | \
|
|
((Flags & EWX_REBOOT) ? EWX_WINLOGON_OLD_REBOOT : 0) | \
|
|
((Flags & EWX_POWEROFF) ? EWX_WINLOGON_OLD_POWEROFF : 0)
|
|
|
|
|
|
BOOLEAN SasMessages = TRUE;
|
|
|
|
//
|
|
// For checking page file
|
|
//
|
|
|
|
extern TCHAR szMemMan[];
|
|
|
|
extern TCHAR szNoPageFile[];
|
|
|
|
//
|
|
// For migration
|
|
//
|
|
|
|
TCHAR szAdminName[ MAX_STRING_BYTES ];
|
|
|
|
//
|
|
// Local Prototypes
|
|
//
|
|
|
|
int
|
|
DoLockWksta(
|
|
PGLOBALS pGlobals,
|
|
BOOL ScreenSaverInvoked);
|
|
|
|
int
|
|
DoScreenSaver(
|
|
PGLOBALS pGlobals,
|
|
BOOL WkstaLocked);
|
|
|
|
void
|
|
WinsrvNotify(
|
|
PGLOBALS pGlobals,
|
|
DWORD SasType);
|
|
|
|
BOOL
|
|
LoggedonDlgInit(
|
|
HWND hDlg
|
|
);
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: DropWorkingSet
|
|
//
|
|
// Synopsis: Reduce working set when we're
|
|
//
|
|
// Arguments: (none)
|
|
//
|
|
// History: 11-04-94 RichardW Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
DropWorkingSet(void)
|
|
{
|
|
NTSTATUS Status;
|
|
QUOTA_LIMITS Quota;
|
|
|
|
Status = NtQueryInformationProcess( NtCurrentProcess(),
|
|
ProcessQuotaLimits,
|
|
&Quota,
|
|
sizeof(QUOTA_LIMITS),
|
|
NULL );
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Quota.MinimumWorkingSetSize = 0xFFFFFFFF;
|
|
Quota.MaximumWorkingSetSize = 0xFFFFFFFF;
|
|
|
|
NtSetInformationProcess(NtCurrentProcess(),
|
|
ProcessQuotaLimits,
|
|
&Quota,
|
|
sizeof(QUOTA_LIMITS) );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: InitializeGinaDll
|
|
//
|
|
// Synopsis: Initializes the gina
|
|
//
|
|
// Arguments: [pGlobals] --
|
|
//
|
|
// History: 10-17-94 RichardW Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL
|
|
InitializeGinaDll(PGLOBALS pGlobals)
|
|
{
|
|
PGINASESSION pGina;
|
|
|
|
pGina = pGlobals->pGina;
|
|
|
|
#if DBG
|
|
if (TEST_FLAG(GinaBreakFlags, BREAK_INITIALIZE))
|
|
{
|
|
DebugLog((DEB_TRACE, "About to call WlxInitialize(%ws, 1, NULL, @%#x, @%#x)\n",
|
|
pGlobals->WindowStation.pszWinsta,
|
|
&WlxDispatchTable, &pGina->pGinaContext));
|
|
|
|
DebugBreak();
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
// Perversely, this may not return. The GINA may in fact call SASNotify
|
|
// immediately, so update the state before we go in:
|
|
//
|
|
|
|
pGlobals->WinlogonState = Winsta_NoOne;
|
|
DebugLog((DEB_TRACE_STATE, "InitGina: State is %d %s\n", Winsta_NoOne, GetState(Winsta_NoOne)));
|
|
|
|
if (!pGina->pWlxInitialize( pGlobals->WindowStation.pszWinsta,
|
|
pGlobals,
|
|
NULL,
|
|
(PVOID) &WlxDispatchTable,
|
|
&pGina->pGinaContext))
|
|
{
|
|
//
|
|
// If the GINA failed to init, we're dead. bugcheck time:
|
|
//
|
|
|
|
ExitProcess( EXIT_GINA_INIT_ERROR );
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SASRouter
|
|
//
|
|
// Synopsis: Routes an SAS event to the appropriate recipient
|
|
//
|
|
// Arguments: [pGlobals] --
|
|
// [SasType] --
|
|
//
|
|
// History: 8-24-94 RichardW Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
SASRouter( PGLOBALS pGlobals,
|
|
DWORD SasType )
|
|
{
|
|
|
|
|
|
if (!TestSasMessages())
|
|
{
|
|
QueueSasEvent(SasType);
|
|
return;
|
|
}
|
|
|
|
pGlobals->SasType = SasType;
|
|
|
|
if (!IsSASState(pGlobals->WinlogonState))
|
|
{
|
|
if (IsDisplayState(pGlobals->WinlogonState) ||
|
|
(pGlobals->WinlogonState == Winsta_WaitForShutdown) ||
|
|
(pGlobals->WinlogonState == Winsta_InShutdownDlg) ||
|
|
(pGlobals->WinlogonState == Winsta_Locked))
|
|
{
|
|
DebugLog((DEB_TRACE, "In state %s, sending kill message to window\n",
|
|
GetState(pGlobals->WinlogonState)));
|
|
|
|
if (!SendSasToTopWindow(pGlobals, SasType))
|
|
DebugLog((DEB_WARN, "No window to send SAS notice to?\n"));
|
|
}
|
|
|
|
//
|
|
// If this was a timeout message,
|
|
if ((SasType == WLX_SAS_TYPE_SCRNSVR_TIMEOUT) ||
|
|
(SasType == WLX_SAS_TYPE_TIMEOUT) )
|
|
{
|
|
//
|
|
// We do *not* change state on a timeout!
|
|
//
|
|
return;
|
|
}
|
|
|
|
ChangeStateForSAS(pGlobals);
|
|
|
|
|
|
if (!pGlobals->ScreenSaverActive)
|
|
{
|
|
SetActiveDesktop(&pGlobals->WindowStation, Desktop_Winlogon);
|
|
}
|
|
|
|
//
|
|
// We should be in one of the three base states now:
|
|
//
|
|
|
|
DebugLog((DEB_TRACE_STATE, "SASRouter: In state %s\n", GetState(pGlobals->WinlogonState)));
|
|
switch (pGlobals->WinlogonState)
|
|
{
|
|
case Winsta_NoOne_SAS:
|
|
case Winsta_LoggedOn_SAS:
|
|
case Winsta_Locked_SAS:
|
|
case Winsta_WaitForLogoff:
|
|
case Winsta_WaitForShutdown:
|
|
case Winsta_InShutdownDlg:
|
|
DisableSasMessages();
|
|
break;
|
|
|
|
|
|
default:
|
|
DebugLog((DEB_ERROR, "SASRouter: Incorrect state %d, %s.\n",
|
|
pGlobals->WinlogonState, GetState(pGlobals->WinlogonState)));
|
|
break;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We are already handling an SAS attempt.
|
|
//
|
|
// Note: This may fail. There may not be a window currently to
|
|
// receive the message. Life is tough that way. The SAS will be
|
|
// *dropped*.
|
|
//
|
|
|
|
DebugLog((DEB_TRACE, "Sending SAS %s to top window\n", SASName(SasType)));
|
|
|
|
SendSasToTopWindow(pGlobals, SasType);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CADNotify
|
|
//
|
|
// Synopsis: Called by sas.c, this is the entrypoint for a Ctrl-Alt-Del
|
|
// call. Expanded to handle all notification from winsrv.
|
|
//
|
|
// Arguments: [pGlobals] --
|
|
// [SasType] --
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 10-17-94 RichardW Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
CADNotify(
|
|
PGLOBALS pGlobals,
|
|
DWORD SasType)
|
|
{
|
|
DebugLog((DEB_TRACE, "Received SAS from winsrv, code %d (%s)\n", SasType, SASName(SasType)));
|
|
if (SasType == WLX_SAS_TYPE_USER_LOGOFF)
|
|
{
|
|
WinsrvNotify(pGlobals, SasType);
|
|
}
|
|
else if (pGlobals->ForwardCAD)
|
|
{
|
|
SASRouter(pGlobals, SasType);
|
|
}
|
|
}
|
|
|
|
PWSTR
|
|
AllocAndDuplicateString(
|
|
PWSTR pszString)
|
|
{
|
|
int len;
|
|
PWSTR pszNewString;
|
|
|
|
if (!pszString)
|
|
{
|
|
return(NULL);
|
|
}
|
|
|
|
len = (wcslen(pszString) + 1) * sizeof(WCHAR);
|
|
|
|
pszNewString = LocalAlloc(LMEM_FIXED, len);
|
|
if (pszNewString)
|
|
{
|
|
CopyMemory(pszNewString, pszString, len);
|
|
}
|
|
|
|
return(pszNewString);
|
|
|
|
}
|
|
|
|
PWSTR
|
|
AllocAndDuplicateStrings(
|
|
PWSTR pszStrings)
|
|
{
|
|
DWORD len;
|
|
PWSTR pszNewStrings;
|
|
|
|
if (!pszStrings)
|
|
{
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
len = LocalSize (pszStrings);
|
|
|
|
pszNewStrings = LocalAlloc(LPTR, len);
|
|
if (pszNewStrings)
|
|
{
|
|
CopyMemory(pszNewStrings, pszStrings, len);
|
|
}
|
|
|
|
return(pszNewStrings);
|
|
|
|
}
|
|
|
|
|
|
PVOID
|
|
CopyEnvironment(
|
|
PVOID pEnv)
|
|
{
|
|
MEMORY_BASIC_INFORMATION mbi;
|
|
PVOID pNew;
|
|
|
|
if (VirtualQueryEx(
|
|
GetCurrentProcess(),
|
|
pEnv,
|
|
&mbi,
|
|
sizeof(mbi) ) )
|
|
{
|
|
pNew = VirtualAlloc(NULL,
|
|
mbi.RegionSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
if (pNew)
|
|
{
|
|
CopyMemory(pNew, pEnv, mbi.RegionSize);
|
|
return(pNew);
|
|
}
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: LogonAttempt
|
|
//
|
|
// Synopsis: Handles a logon attempt.
|
|
//
|
|
// Arguments: [pGlobals] --
|
|
//
|
|
// History: 10-17-94 RichardW Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
int
|
|
LogonAttempt(
|
|
PGLOBALS pGlobals)
|
|
{
|
|
DWORD WlxResult;
|
|
PGINASESSION pGina;
|
|
WLX_MPR_NOTIFY_INFO MprInfo;
|
|
PWLX_PROFILE_V2_0 pProfileInfo;
|
|
PSID pLogonSid;
|
|
DWORD Options;
|
|
HANDLE hToken;
|
|
HANDLE uh;
|
|
int MprRet;
|
|
|
|
|
|
pGina = pGlobals->pGina;
|
|
|
|
pLogonSid = CreateLogonSid(NULL);
|
|
|
|
#if DBG
|
|
if (TEST_FLAG(GinaBreakFlags, BREAK_LOGGEDOUT))
|
|
{
|
|
DebugLog((DEB_TRACE, "About to call WlxLoggedOutSAS(%#x, %d, @%#x, @%x,\n",
|
|
pGina->pGinaContext, pGlobals->SasType, &pGlobals->LogonId, pLogonSid));
|
|
DebugLog((DEB_TRACE, " @%#x, @%#x, @%#x, @%#x)\n", &Options, &hToken,
|
|
&MprInfo, &pProfileInfo));
|
|
|
|
DebugBreak();
|
|
}
|
|
#endif
|
|
|
|
WlxSetTimeout(pGlobals, 120);
|
|
|
|
WlxResult = pGina->pWlxLoggedOutSAS(pGina->pGinaContext,
|
|
pGlobals->SasType,
|
|
&pGlobals->LogonId,
|
|
pLogonSid,
|
|
&Options,
|
|
&hToken,
|
|
&MprInfo,
|
|
&pProfileInfo );
|
|
|
|
DebugLog((DEB_TRACE, "WlxLoggedOutSAS returned %d, %s\n", WlxResult, WlxName(WlxResult)));
|
|
|
|
if (WlxResult != WLX_SAS_ACTION_LOGON )
|
|
{
|
|
DebugLog((DEB_TRACE_STATE, "LogonAttempt: Resetting state to %s\n", GetState(Winsta_NoOne)));
|
|
|
|
pGlobals->WinlogonState = Winsta_NoOne;
|
|
|
|
DeleteLogonSid(pLogonSid);
|
|
|
|
return(WlxResult);
|
|
}
|
|
|
|
//
|
|
// Okay, someone logged on. This, this is interesting.
|
|
//
|
|
|
|
if (MprInfo.pszUserName)
|
|
{
|
|
pGlobals->UserName = AllocAndDuplicateString(MprInfo.pszUserName);
|
|
}
|
|
|
|
|
|
MprRet = MprLogonNotify(
|
|
pGlobals,
|
|
NULL,
|
|
MprInfo.pszUserName,
|
|
MprInfo.pszDomain,
|
|
MprInfo.pszPassword,
|
|
MprInfo.pszOldPassword,
|
|
&pGlobals->LogonId,
|
|
&pGlobals->LogonScripts);
|
|
|
|
DestroyMprInfo(&MprInfo);
|
|
|
|
//
|
|
// Wait on the font loading thread here, so we don't inadvertantly re-enter
|
|
// the stuff in user that gets confused.
|
|
//
|
|
|
|
if ( hFontThread )
|
|
{
|
|
WaitForSingleObject( hFontThread, INFINITE );
|
|
|
|
CloseHandle( hFontThread );
|
|
|
|
hFontThread = NULL;
|
|
|
|
}
|
|
|
|
SecurityChangeUser(pGlobals, hToken, NULL, pLogonSid, TRUE);
|
|
|
|
if (!TEST_FLAG(Options, WLX_LOGON_OPT_NO_PROFILE))
|
|
{
|
|
if (pProfileInfo) {
|
|
|
|
if (pProfileInfo->pszProfile)
|
|
{
|
|
pGlobals->UserProfile.ProfilePath =
|
|
AllocAndExpandEnvironmentStrings(pProfileInfo->pszProfile);
|
|
LocalFree(pProfileInfo->pszProfile);
|
|
}
|
|
else
|
|
{
|
|
pGlobals->UserProfile.ProfilePath = NULL;
|
|
}
|
|
|
|
if (pProfileInfo->dwType >= WLX_PROFILE_TYPE_V2_0) {
|
|
if (pProfileInfo->pszPolicy)
|
|
{
|
|
pGlobals->UserProfile.PolicyPath =
|
|
AllocAndDuplicateString(pProfileInfo->pszPolicy);
|
|
|
|
LocalFree(pProfileInfo->pszPolicy);
|
|
}
|
|
else
|
|
{
|
|
pGlobals->UserProfile.PolicyPath = NULL;
|
|
}
|
|
|
|
if (pProfileInfo->pszNetworkDefaultUserProfile)
|
|
{
|
|
pGlobals->UserProfile.NetworkDefaultUserProfile =
|
|
AllocAndDuplicateString(pProfileInfo->pszNetworkDefaultUserProfile);
|
|
|
|
LocalFree(pProfileInfo->pszNetworkDefaultUserProfile);
|
|
}
|
|
else
|
|
{
|
|
pGlobals->UserProfile.NetworkDefaultUserProfile = NULL;
|
|
}
|
|
|
|
if (pProfileInfo->pszServerName)
|
|
{
|
|
pGlobals->UserProfile.ServerName =
|
|
AllocAndDuplicateString(pProfileInfo->pszServerName);
|
|
|
|
LocalFree(pProfileInfo->pszServerName);
|
|
}
|
|
else
|
|
{
|
|
pGlobals->UserProfile.ServerName = NULL;
|
|
}
|
|
|
|
if (pProfileInfo->pszEnvironment)
|
|
{
|
|
pGlobals->UserProfile.Environment =
|
|
AllocAndDuplicateStrings(pProfileInfo->pszEnvironment);
|
|
|
|
LocalFree(pProfileInfo->pszEnvironment);
|
|
}
|
|
else
|
|
{
|
|
pGlobals->UserProfile.Environment = NULL;
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
pGlobals->UserProfile.PolicyPath = NULL;
|
|
pGlobals->UserProfile.NetworkDefaultUserProfile = NULL;
|
|
pGlobals->UserProfile.ServerName = NULL;
|
|
pGlobals->UserProfile.Environment = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
DebugLog((DEB_TRACE_PROFILE, "Using initial profile path of %ws\n", pGlobals->UserProfile.ProfilePath));
|
|
|
|
LocalFree(pProfileInfo);
|
|
|
|
//
|
|
// Load profile, set environment variables, etc.
|
|
//
|
|
|
|
if (SetupUserEnvironment(pGlobals))
|
|
{
|
|
OpenIniFileUserMapping(pGlobals);
|
|
|
|
uh = ImpersonateUser(&pGlobals->UserProcessData, NULL);
|
|
|
|
SetWindowStationUser(pGlobals->WindowStation.hwinsta, &pGlobals->LogonId,
|
|
pGlobals->UserProcessData.UserSid,
|
|
RtlLengthSid(pGlobals->UserProcessData.UserSid));
|
|
|
|
StopImpersonating(uh);
|
|
|
|
//
|
|
// Update the window station lock so that apps can start.
|
|
//
|
|
|
|
UnlockWindowStation(pGlobals->WindowStation.hwinsta);
|
|
LockWindowStation(pGlobals->WindowStation.hwinsta);
|
|
|
|
//
|
|
// allocate floppies and CDRoms (if so configured)
|
|
//
|
|
|
|
RmvAllocateRemovableMedia( pLogonSid );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Whoops, something went wrong. we *must* log the user
|
|
// out. We do this by passing LOGOFF back to mainloop.
|
|
//
|
|
|
|
WlxResult = WLX_SAS_ACTION_LOGOFF;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
return(WlxResult);
|
|
|
|
}
|
|
|
|
/****************************************************************************\
|
|
*
|
|
* FUNCTION: DisplayPreShellLogonMessages
|
|
*
|
|
* PURPOSE: Displays any security warnings to the user after a successful logon
|
|
* The messages are displayed before the shell starts
|
|
*
|
|
* RETURNS: DLG_SUCCESS - the dialogs were displayed successfully.
|
|
* DLG_INTERRUPTED() - a set defined in winlogon.h
|
|
*
|
|
* NOTE: Screen-saver timeouts are handled by our parent dialog so this
|
|
* routine should never return DLG_SCREEN_SAVER_TIMEOUT
|
|
*
|
|
* HISTORY:
|
|
*
|
|
* 12-09-91 Davidc Created.
|
|
*
|
|
\****************************************************************************/
|
|
|
|
int
|
|
DisplayPreShellLogonMessages(
|
|
PGLOBALS pGlobals
|
|
)
|
|
{
|
|
int Result;
|
|
|
|
if (PageFilePopup) {
|
|
HKEY hkeyMM;
|
|
DWORD dwTempFile, cbTempFile, dwType;
|
|
|
|
//
|
|
// WinLogon created a temp page file. If a previous user has not
|
|
// created a real one already, then inform this user to do so.
|
|
//
|
|
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szMemMan, 0, KEY_READ,
|
|
&hkeyMM) == ERROR_SUCCESS) {
|
|
|
|
cbTempFile = sizeof(dwTempFile);
|
|
if (RegQueryValueEx (hkeyMM, szNoPageFile, NULL, &dwType,
|
|
(LPBYTE) &dwTempFile, &cbTempFile) != ERROR_SUCCESS ||
|
|
dwType != REG_DWORD || cbTempFile != sizeof(dwTempFile)) {
|
|
dwTempFile = 0;
|
|
}
|
|
|
|
RegCloseKey(hkeyMM);
|
|
} else
|
|
dwTempFile = 0;
|
|
|
|
if (dwTempFile == 1) {
|
|
|
|
WlxSetTimeout(pGlobals, TIMEOUT_NONE);
|
|
|
|
Result = TimeoutMessageBox(
|
|
pGlobals,
|
|
NULL,
|
|
IDS_NO_PAGING_FILE,
|
|
IDS_LIMITED_RESOURCES,
|
|
MB_OK | MB_ICONSTOP
|
|
);
|
|
|
|
if (Result == WLX_DLG_INPUT_TIMEOUT) {
|
|
return(Result);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(DLG_SUCCESS);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: DoStartShell
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: [pGlobals] --
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Signals:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 9-30-94 RichardW Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL
|
|
DoStartShell(
|
|
PGLOBALS pGlobals
|
|
)
|
|
{
|
|
PGINASESSION pGina;
|
|
HANDLE hImp;
|
|
PVOID pNewEnvironment;
|
|
|
|
(void) DisplayPreShellLogonMessages(pGlobals);
|
|
|
|
//
|
|
// If not logging in as Guest, System or Administrator then check for
|
|
// migration of Windows 3.1 configuration inforation.
|
|
//
|
|
|
|
if (szAdminName[ 0 ] == TEXT('\0'))
|
|
{
|
|
LoadString(NULL, IDS_ADMIN_ACCOUNT_NAME, szAdminName, sizeof(szAdminName));
|
|
}
|
|
|
|
|
|
if (!IsUserAGuest(pGlobals) &&
|
|
_wcsicmp(pGlobals->UserName, szAdminName)
|
|
)
|
|
{
|
|
Windows31Migration(pGlobals);
|
|
}
|
|
|
|
|
|
pGina = pGlobals->pGina;
|
|
|
|
|
|
//
|
|
// Play the user's logon sound
|
|
//
|
|
|
|
if (pGlobals->PlaySound ||
|
|
pGlobals->MigrateSoundEvents ||
|
|
pGlobals->MigrateMidiUser)
|
|
{
|
|
BOOL fBeep;
|
|
|
|
if (OpenIniFileUserMapping(pGlobals))
|
|
{
|
|
hImp = ImpersonateUser(&pGlobals->UserProcessData, NULL);
|
|
|
|
//
|
|
// Migrate Users MIDI settings
|
|
//
|
|
|
|
if (pGlobals->MigrateMidiUser) {
|
|
(*(pGlobals->MigrateMidiUser))();
|
|
}
|
|
|
|
if (pGlobals->MigrateSoundEvents) {
|
|
(*(pGlobals->MigrateSoundEvents))();
|
|
}
|
|
|
|
if (pGlobals->PlaySound) {
|
|
|
|
//
|
|
// Whenever a user logs in, have WINMM.DLL check if there
|
|
// are any sound events within the [SOUNDS] section of
|
|
// CONTROL.INI that haven't been ported into HKCU/AppEvents.
|
|
// If there are, migrate those schemes to their new home.
|
|
// This must be done before the upcoming PlaySound() call,
|
|
// as PlaySound() uses the HKCU/AppEvents schemes-listing
|
|
// to resolve an SND_ALIAS_ID request.
|
|
//
|
|
|
|
if (!SystemParametersInfo(SPI_GETBEEP, 0, &fBeep, FALSE)) {
|
|
// Failed to get hold of beep setting. Should we be
|
|
// noisy or quiet? We have to choose one value...
|
|
fBeep = TRUE;
|
|
}
|
|
|
|
if (fBeep) {
|
|
(*(pGlobals->PlaySound))((LPCSTR)SND_ALIAS_SYSTEMSTART,
|
|
NULL,
|
|
SND_ALIAS_ID | SND_ASYNC | SND_NODEFAULT);
|
|
}
|
|
}
|
|
|
|
StopImpersonating(hImp);
|
|
|
|
CloseIniFileUserMapping(pGlobals);
|
|
}
|
|
|
|
}
|
|
|
|
WlxSetTimeout(pGlobals, 120);
|
|
|
|
pNewEnvironment = CopyEnvironment(pGlobals->UserProcessData.pEnvironment);
|
|
|
|
#if DBG
|
|
if (TEST_FLAG(GinaBreakFlags, BREAK_ACTIVATE))
|
|
{
|
|
DebugLog((DEB_TRACE, "About to call WlxActivateUserShell(%#x, %ws, %ws, %#x)\n",
|
|
pGina->pGinaContext, APPLICATION_DESKTOP_PATH,
|
|
pGlobals->LogonScripts, NULL));
|
|
DebugBreak();
|
|
}
|
|
#endif
|
|
return( pGina->pWlxActivateUserShell( pGina->pGinaContext,
|
|
APPLICATION_DESKTOP_PATH,
|
|
pGlobals->LogonScripts,
|
|
pNewEnvironment) );
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: HandleLoggedOn
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: [pGlobals] --
|
|
// [SasType] --
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Signals:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 9-30-94 RichardW Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HandleLoggedOn(
|
|
PGLOBALS pGlobals,
|
|
DWORD SasType)
|
|
{
|
|
DWORD Result;
|
|
WLX_MPR_NOTIFY_INFO MprInfo;
|
|
PGINASESSION pGina;
|
|
int Flags;
|
|
int MprRet;
|
|
int LogoffResult;
|
|
|
|
pGina = pGlobals->pGina;
|
|
|
|
SetActiveDesktop(&pGlobals->WindowStation, Desktop_Winlogon);
|
|
|
|
ZeroMemory(&MprInfo, sizeof(MprInfo));
|
|
|
|
WlxSetTimeout(pGlobals, 120);
|
|
|
|
#if DBG
|
|
if (TEST_FLAG(GinaBreakFlags, BREAK_LOGGEDON))
|
|
{
|
|
DebugLog((DEB_TRACE, "About to call WlxLoggedOnSAS( %#x, %d, NULL)\n",
|
|
pGina->pGinaContext, pGlobals->SasType));
|
|
DebugBreak();
|
|
}
|
|
#endif
|
|
|
|
Result = pGina->pWlxLoggedOnSAS( pGina->pGinaContext,
|
|
SasType,
|
|
NULL );
|
|
|
|
WlxSetTimeout(pGlobals, TIMEOUT_NONE);
|
|
|
|
DebugLog((DEB_TRACE, "WlxLoggedOnSAS returned %d, %s\n", Result, WlxName(Result)));
|
|
pGlobals->LastGinaRet = Result;
|
|
|
|
//
|
|
// if a new SAS has come in while we were processing that one, repost it and
|
|
// pick it up in LoggedOnDlgProc.
|
|
//
|
|
|
|
if ( pGlobals->SasType != SasType )
|
|
{
|
|
DebugLog((DEB_TRACE, "New SAS (%d: %s) came in while handling (%d: %s). Routing it now\n",
|
|
pGlobals->SasType, SASName(pGlobals->SasType),
|
|
SasType, SASName(SasType) ));
|
|
|
|
SASRouter( pGlobals, pGlobals->SasType );
|
|
|
|
}
|
|
|
|
if (Result == WLX_SAS_ACTION_LOCK_WKSTA)
|
|
{
|
|
return (DoLockWksta(pGlobals, FALSE));
|
|
}
|
|
|
|
if ((Result == WLX_SAS_ACTION_TASKLIST) ||
|
|
(Result == WLX_SAS_ACTION_NONE))
|
|
{
|
|
if ((pGlobals->WinlogonState != Winsta_WaitForLogoff) &&
|
|
(pGlobals->WinlogonState != Winsta_WaitForShutdown) &&
|
|
(pGlobals->WinlogonState != Winsta_InShutdownDlg) )
|
|
{
|
|
SetActiveDesktop(&pGlobals->WindowStation, Desktop_Application);
|
|
}
|
|
if (Result == WLX_SAS_ACTION_TASKLIST)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
TickleMessenger();
|
|
|
|
return(Result);
|
|
|
|
}
|
|
|
|
switch (Result)
|
|
{
|
|
case WLX_SAS_ACTION_LOGOFF:
|
|
Flags = EWX_LOGOFF;
|
|
break;
|
|
|
|
case WLX_SAS_ACTION_FORCE_LOGOFF:
|
|
Flags = EWX_LOGOFF | EWX_FORCE;
|
|
break;
|
|
|
|
case WLX_SAS_ACTION_SHUTDOWN:
|
|
Flags = EWX_LOGOFF | EWX_WINLOGON_OLD_SHUTDOWN;
|
|
break;
|
|
|
|
case WLX_SAS_ACTION_SHUTDOWN_REBOOT:
|
|
Flags = EWX_LOGOFF | EWX_WINLOGON_OLD_SHUTDOWN | EWX_WINLOGON_OLD_REBOOT;
|
|
break;
|
|
|
|
case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF:
|
|
Flags = EWX_LOGOFF | EWX_WINLOGON_OLD_SHUTDOWN | EWX_WINLOGON_OLD_POWEROFF;
|
|
break;
|
|
|
|
default:
|
|
DebugLog((DEB_ERROR, "Incorrect result (%d) from WlxLoggedOnSAS\n", Result));
|
|
return(0);
|
|
}
|
|
|
|
|
|
LogoffResult = InitiateLogoff(pGlobals, Flags);
|
|
if (LogoffResult == DLG_FAILURE)
|
|
{
|
|
return(WLX_SAS_ACTION_NONE);
|
|
}
|
|
|
|
return(Result);
|
|
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: DoLockWksta
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments: [pGlobals] --
|
|
//
|
|
// History: 9-16-94 RichardW Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
int
|
|
DoLockWksta(
|
|
PGLOBALS pGlobals,
|
|
BOOL ScreenSaverInvoked)
|
|
{
|
|
int Result;
|
|
PGINASESSION pGina;
|
|
|
|
pGlobals->WinlogonState = Winsta_Locked;
|
|
DebugLog((DEB_TRACE_STATE, "DoLockWksta: Setting state to %s\n", GetState(Winsta_Locked)));
|
|
|
|
pGina = pGlobals->pGina;
|
|
|
|
LockWindowStation(pGlobals->WindowStation.hwinsta);
|
|
|
|
do
|
|
{
|
|
|
|
pGlobals->WinlogonState = Winsta_Locked_Display;
|
|
DebugLog((DEB_TRACE_STATE, "DoLockWksta: Setting state to %s\n",
|
|
GetState(Winsta_Locked_Display)));
|
|
#if DBG
|
|
if (TEST_FLAG(GinaBreakFlags, BREAK_DISPLAYLOCKED))
|
|
{
|
|
DebugLog((DEB_TRACE, "About to call WlxDisplayLockedNotice( %#x )\n",
|
|
pGina->pGinaContext ));
|
|
DebugBreak();
|
|
}
|
|
#endif
|
|
//
|
|
// No input timeout
|
|
//
|
|
|
|
WlxSetTimeout(pGlobals, TIMEOUT_NONE);
|
|
|
|
pGina->pWlxDisplayLockedNotice( pGina->pGinaContext );
|
|
|
|
DebugLog((DEB_TRACE, "Out of DisplayLockedNotice, SAS = %s\n",
|
|
SASName(pGlobals->SasType)));
|
|
|
|
if (pGlobals->SasType == WLX_SAS_TYPE_SCRNSVR_TIMEOUT)
|
|
{
|
|
//
|
|
// If we were invoked as part of a secure screen saver,
|
|
// then this timeout means that we should return to it
|
|
// and let it cycle.
|
|
//
|
|
if (ScreenSaverInvoked)
|
|
{
|
|
return(WLX_SAS_ACTION_NONE);
|
|
}
|
|
|
|
//
|
|
// Invoke the screen saver:
|
|
//
|
|
|
|
if (DoScreenSaver(pGlobals, TRUE) >= 0)
|
|
{
|
|
|
|
//
|
|
// Jump right back to the top.
|
|
//
|
|
|
|
Result = WLX_SAS_ACTION_NONE;
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// A return of -1 indicates that some other SAS occurred,
|
|
// e.g. a Logoff, or a GINA specific SAS. Fall through to
|
|
// the default handling.
|
|
//
|
|
|
|
}
|
|
|
|
//
|
|
// An unfortunate label, but things get awfully convoluted switching
|
|
// between the screen saver and the locked state. The screen saver
|
|
// has stopped due to a SAS, and here is where we figure out what to
|
|
// do. This is jumped to from below, if the WkstaLocked dialog
|
|
// ended with a screen saver timeout.
|
|
//
|
|
|
|
|
|
ResetLockCall:
|
|
|
|
if (pGlobals->SasType == WLX_SAS_TYPE_USER_LOGOFF)
|
|
{
|
|
if (pGlobals->LogoffFlags & EWX_WINLOGON_API_SHUTDOWN)
|
|
{
|
|
pGlobals->WinlogonState = Winsta_Shutdown;
|
|
}
|
|
return(WLX_SAS_ACTION_LOGOFF);
|
|
}
|
|
|
|
|
|
#if DBG
|
|
if (TEST_FLAG(GinaBreakFlags, BREAK_WKSTALOCKED))
|
|
{
|
|
DebugLog((DEB_TRACE, "About to call WlxWkstaLockedSAS( %#x, %d )\n",
|
|
pGina->pGinaContext, pGlobals->SasType ));
|
|
DebugBreak();
|
|
}
|
|
#endif
|
|
WlxSetTimeout(pGlobals, 120);
|
|
|
|
Result = pGina->pWlxWkstaLockedSAS( pGina->pGinaContext, pGlobals->SasType );
|
|
|
|
WlxSetTimeout(pGlobals, TIMEOUT_NONE);
|
|
|
|
DebugLog((DEB_TRACE, "WlxWkstaLockedSAS returned %d, %s\n", Result, WlxName(Result)));
|
|
|
|
pGlobals->LastGinaRet = Result;
|
|
|
|
if ( (Result == WLX_SAS_ACTION_NONE) &&
|
|
(pGlobals->SasType == WLX_SAS_TYPE_SCRNSVR_TIMEOUT ) )
|
|
{
|
|
//
|
|
// The GINA was interrupted by a screen saver timeout. If
|
|
// we were invoked by a screen saver, we should return immediately,
|
|
// otherwise, run the screen saver. Same as before when the display
|
|
// call timed out.
|
|
//
|
|
|
|
if (ScreenSaverInvoked)
|
|
{
|
|
return(WLX_SAS_ACTION_NONE);
|
|
}
|
|
|
|
//
|
|
// A return of -1 indicates that the screen saver terminated
|
|
// due to some other SAS. Jump back up to the point where we
|
|
// handle that, so that we have one point where we do things
|
|
// correctly.
|
|
//
|
|
|
|
if ( DoScreenSaver( pGlobals, TRUE ) < 0 )
|
|
{
|
|
goto ResetLockCall;
|
|
|
|
}
|
|
|
|
//
|
|
// Otherwise, fall through, and loop again.
|
|
//
|
|
|
|
|
|
}
|
|
|
|
} while (Result == WLX_SAS_ACTION_NONE);
|
|
|
|
|
|
if (Result == WLX_SAS_ACTION_FORCE_LOGOFF)
|
|
{
|
|
InitiateLogoff(pGlobals, EWX_LOGOFF | EWX_FORCE);
|
|
}
|
|
else
|
|
{
|
|
SetActiveDesktop( &pGlobals->WindowStation, Desktop_Application );
|
|
|
|
TickleMessenger();
|
|
}
|
|
|
|
return(Result);
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: DoScreenSaver
|
|
//
|
|
// Synopsis: Starts up the screen saver
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: [pGlobals] --
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Signals:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 10-13-94 RichardW Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
int
|
|
DoScreenSaver(
|
|
PGLOBALS pGlobals,
|
|
BOOL WkstaLocked)
|
|
{
|
|
int Result;
|
|
BOOL FastUnlock;
|
|
|
|
|
|
//
|
|
// WkstaLocked indicates that we were called by the DoLockWksta
|
|
// path. This means that we should not recursively call them, since
|
|
// there is no stopping case and we could chew up a lot of stack.
|
|
// So, we loop here, but we can break out if RunScreenSaver doesn't
|
|
// return Wksta locked.
|
|
//
|
|
|
|
//
|
|
// FastUnlock determines if we allow a grace period or not. If the
|
|
// wksta is locked coming in, then it is not allowed at all. If it is
|
|
// not locked on entry, then we allow it once.
|
|
//
|
|
|
|
FastUnlock = !WkstaLocked;
|
|
|
|
do
|
|
{
|
|
Result = RunScreenSaver(pGlobals, FALSE, FastUnlock);
|
|
|
|
FastUnlock = FALSE;
|
|
|
|
if (Result == WLX_SAS_ACTION_LOCK_WKSTA)
|
|
{
|
|
//
|
|
// Ok, it's a secure screen saver. If we are already locked,
|
|
// break and return
|
|
//
|
|
if (WkstaLocked)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Ok, it's not. Invoke the lock code ourselves, but tell it
|
|
// that it's being called from the screen saver path.
|
|
//
|
|
|
|
Result = DoLockWksta(pGlobals, TRUE);
|
|
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Loop clause: we only get here if we have invoked DoLockWksta.
|
|
// Loop only if it return WLX_SAS_ACTION_NONE, not unlock or force
|
|
// logoff.
|
|
//
|
|
|
|
} while (Result == WLX_SAS_ACTION_NONE);
|
|
|
|
if ( (Result == -1) || (Result == WLX_SAS_ACTION_LOGOFF) )
|
|
{
|
|
return( Result );
|
|
}
|
|
else if (Result == WLX_SAS_ACTION_FORCE_LOGOFF)
|
|
{
|
|
return(Result);
|
|
}
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* FUNCTION: LogoffWaitDlgProc
|
|
*
|
|
* PURPOSE: Processes messages for the forced logoff wait dialog
|
|
*
|
|
* RETURNS:
|
|
* DLG_FAILURE - the dialog could not be displayed
|
|
* DLG_INTERRUPTED() - this is a set of possible interruptions (see winlogon.h)
|
|
*
|
|
* HISTORY:
|
|
*
|
|
* 05-09-92 Davidc Created.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
BOOL
|
|
CALLBACK
|
|
LogoffWaitDlgProc(
|
|
HWND hDlg,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
switch (message) {
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
EnableSasMessages(hDlg);
|
|
|
|
SetWindowLong(hDlg, GWL_USERDATA, lParam);
|
|
|
|
CentreWindow(hDlg);
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
// We didn't process this message
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
WaitForForceLogoff(
|
|
HWND hWnd,
|
|
PGLOBALS pGlobals)
|
|
{
|
|
int Result;
|
|
|
|
do
|
|
{
|
|
SetActiveDesktop( &pGlobals->WindowStation, Desktop_Winlogon );
|
|
|
|
WlxSetTimeout(pGlobals, TIMEOUT_NONE);
|
|
|
|
Result = WlxDialogBoxParam( pGlobals,
|
|
pGlobals->hInstance,
|
|
MAKEINTRESOURCE(IDD_FORCED_LOGOFF_WAIT),
|
|
hWnd,
|
|
LogoffWaitDlgProc,
|
|
(LONG) pGlobals);
|
|
|
|
} while ( (Result == WLX_DLG_INPUT_TIMEOUT) ||
|
|
(Result == WLX_DLG_SCREEN_SAVER_TIMEOUT) );
|
|
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* FUNCTION: LoggedOnDlgProc
|
|
*
|
|
* PURPOSE: Processes messages for the logged-on control dialog
|
|
*
|
|
* DIALOG RETURNS:
|
|
*
|
|
* DLG_FAILURE - Couldn't bring up the dialog
|
|
* DLG_LOGOFF() - The user logged off
|
|
*
|
|
* NOTES:
|
|
*
|
|
* On entry, it assumed that the winlogon desktop is switched to and the
|
|
* desktop lock is held. This same state exists on exit.
|
|
*
|
|
* HISTORY:
|
|
*
|
|
* 12-09-91 Davidc Created.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
BOOL WINAPI
|
|
LoggedonDlgProc(
|
|
HWND hDlg,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
PGLOBALS pGlobals = (PGLOBALS)GetWindowLong(hDlg, GWL_USERDATA);
|
|
int Result;
|
|
|
|
|
|
switch (message)
|
|
{
|
|
|
|
case WM_INITDIALOG:
|
|
SetWindowLong(hDlg, GWL_USERDATA, lParam);
|
|
pGlobals = (PGLOBALS)lParam;
|
|
|
|
if (!LoggedonDlgInit(hDlg)) {
|
|
EndDialog(hDlg, DLG_FAILURE);
|
|
return(TRUE);
|
|
}
|
|
|
|
// Send ourselves a message so we can hide ourselves without the
|
|
// dialog code trying to force us to be visible
|
|
PostMessage(hDlg, WM_HIDEOURSELVES, 0, 0);
|
|
|
|
//
|
|
//
|
|
// Switch to app desktop and release lock
|
|
//
|
|
|
|
SetActiveDesktop(&pGlobals->WindowStation, Desktop_Application);
|
|
|
|
//
|
|
// Tickle the messenger so it will display any queue'd messages.
|
|
// (This call is a kind of NoOp).
|
|
//
|
|
TickleMessenger();
|
|
|
|
return(TRUE);
|
|
|
|
case WM_HIDEOURSELVES:
|
|
ShowWindow(hDlg, SW_HIDE);
|
|
|
|
DropWorkingSet();
|
|
|
|
return(TRUE);
|
|
|
|
case WLX_WM_SAS:
|
|
|
|
//
|
|
// Disable further SAS events until we decide what to do. If
|
|
// we start another window, they will automagically be forwarded
|
|
// to it. This lets us call right into the individual cases.
|
|
//
|
|
|
|
DisableSasMessages();
|
|
|
|
if (wParam == WLX_SAS_TYPE_SCRNSVR_TIMEOUT)
|
|
{
|
|
Result = DoScreenSaver(pGlobals, FALSE);
|
|
|
|
if ( (Result < 0) || (Result == WLX_SAS_ACTION_LOGOFF) )
|
|
{
|
|
//
|
|
// Ugly case: the screen saver received a SAS event
|
|
// which has interrupted it. So, that SAS is now stored
|
|
// in the globals. We snag it, stuff it in wParam, and
|
|
// FALL THROUGH to the rest of this code.
|
|
//
|
|
|
|
wParam = pGlobals->SasType;
|
|
}
|
|
else if (Result == WLX_SAS_ACTION_FORCE_LOGOFF)
|
|
{
|
|
EndDialog(hDlg, WLX_SAS_ACTION_FORCE_LOGOFF);
|
|
return(TRUE);
|
|
|
|
}
|
|
else
|
|
{
|
|
EnableSasMessages(hDlg);
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Ok, more ugly cases. The user could asynchronously log off
|
|
// while we're in HandleLoggedOn(), in which case we would get
|
|
// the logoff notify in some other dialog, and returned to us
|
|
// here. But, we also have some logoff cases coming through
|
|
// here if we are waiting for the logoff, so if this is not
|
|
// winsrv telling us it's logged the guy off, ask the gina
|
|
// what to do.
|
|
//
|
|
|
|
if ((wParam != WLX_SAS_TYPE_USER_LOGOFF) &&
|
|
(wParam != WLX_SAS_TYPE_TIMEOUT) )
|
|
{
|
|
Result = HandleLoggedOn(pGlobals, (DWORD) wParam);
|
|
}
|
|
else
|
|
{
|
|
Result = -1;
|
|
}
|
|
|
|
|
|
if ((wParam == WLX_SAS_TYPE_USER_LOGOFF ) ||
|
|
(Result == WLX_SAS_ACTION_LOGOFF ) ||
|
|
(Result == WLX_SAS_ACTION_FORCE_LOGOFF ) )
|
|
{
|
|
//
|
|
// If we were shut down by the remote guy, handle that
|
|
//
|
|
if (pGlobals->WinlogonState == Winsta_Shutdown)
|
|
{
|
|
EndDialog(hDlg, pGlobals->LastGinaRet);
|
|
return(TRUE);
|
|
}
|
|
if ((pGlobals->WinlogonState == Winsta_WaitForLogoff) ||
|
|
(wParam == WLX_SAS_TYPE_USER_LOGOFF) )
|
|
{
|
|
if (IsShutdown(pGlobals->LastGinaRet))
|
|
{
|
|
pGlobals->WinlogonState = Winsta_WaitForShutdown;
|
|
}
|
|
else
|
|
pGlobals->WinlogonState = Winsta_NoOne;
|
|
|
|
DebugLog((DEB_TRACE_STATE, "LoggedOnDlg: setting state to %s\n",
|
|
GetState(pGlobals->WinlogonState)));
|
|
|
|
EndDialog(hDlg, pGlobals->LastGinaRet);
|
|
return(TRUE);
|
|
|
|
}
|
|
else
|
|
{
|
|
DebugLog((DEB_TRACE_STATE, "LoggedOnDlg: setting state to WaitForLogoff\n"));
|
|
pGlobals->WinlogonState = Winsta_WaitForLogoff;
|
|
|
|
//
|
|
// If this is a force-logoff, end now, so that we fall
|
|
// through to the special dialog (WaitForForceLogoff())
|
|
//
|
|
|
|
if (Result == WLX_SAS_ACTION_FORCE_LOGOFF)
|
|
{
|
|
EndDialog(hDlg, Result);
|
|
return(TRUE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Now it is perverse. If the user logged off *while the
|
|
// options dialog was up*, they will return NONE, expecting
|
|
// us to deal with it correctly.
|
|
//
|
|
|
|
if (pGlobals->WinlogonState == Winsta_WaitForLogoff)
|
|
{
|
|
if (IsShutdown(pGlobals->LastGinaRet))
|
|
{
|
|
pGlobals->WinlogonState = Winsta_WaitForShutdown;
|
|
}
|
|
else
|
|
pGlobals->WinlogonState = Winsta_NoOne;
|
|
|
|
DebugLog((DEB_TRACE_STATE, "LoggedOnDlg: setting state to %s\n",
|
|
GetState(pGlobals->WinlogonState)));
|
|
|
|
EndDialog(hDlg, pGlobals->LastGinaRet);
|
|
return(TRUE);
|
|
|
|
}
|
|
}
|
|
|
|
EnableSasMessages(hDlg);
|
|
|
|
DropWorkingSet();
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
// We didn't process this message
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* FUNCTION: LoggedonDlgInit
|
|
*
|
|
* PURPOSE: Handles initialization of logged-on dialog
|
|
*
|
|
* RETURNS: TRUE on success, FALSE on failure
|
|
*
|
|
* HISTORY:
|
|
*
|
|
* 12-09-91 Davidc Created.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
BOOL
|
|
LoggedonDlgInit(
|
|
HWND hDlg
|
|
)
|
|
{
|
|
PGLOBALS pGlobals = (PGLOBALS)GetWindowLong(hDlg, GWL_USERDATA);
|
|
|
|
// Set our size to zero so we we don't appear
|
|
SetWindowPos(hDlg, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE |
|
|
SWP_NOREDRAW | SWP_NOZORDER);
|
|
|
|
SetMapperFlag(hDlg, MAPPERFLAG_WINLOGON);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: BlockWaitForUserAction
|
|
//
|
|
// Synopsis: Blocks, waiting for the interactive user to do something, or
|
|
// a SAS to come in from the gina.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: [pGlobals] --
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Signals:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 10-17-94 RichardW Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
int
|
|
BlockWaitForUserAction(PGLOBALS pGlobals)
|
|
{
|
|
int res;
|
|
|
|
SetActiveDesktop(&pGlobals->WindowStation, Desktop_Application);
|
|
WlxSetTimeout(pGlobals, TIMEOUT_NONE);
|
|
res = WlxDialogBoxParam( pGlobals, pGlobals->hInstance,
|
|
MAKEINTRESOURCE(IDD_CONTROL),
|
|
NULL,
|
|
LoggedonDlgProc,
|
|
(LONG) pGlobals) ;
|
|
#if DBG
|
|
if (res == -1)
|
|
{
|
|
DebugLog((DEB_ERROR, "Failed to start LoggedOnDlgProc, %d\n", GetLastError()));
|
|
}
|
|
#endif
|
|
|
|
return(res);
|
|
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: MainLoop
|
|
//
|
|
// Synopsis: Main winlogon loop.
|
|
//
|
|
// Arguments: [pGlobals] --
|
|
//
|
|
// History: 10-17-94 RichardW Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
MainLoop(PGLOBALS pGlobals)
|
|
{
|
|
DWORD WlxResult;
|
|
PGINASESSION pGina;
|
|
int ScreenSaverResult;
|
|
|
|
WlxResult = WLX_SAS_ACTION_NONE;
|
|
pGina = pGlobals->pGina;
|
|
|
|
//
|
|
// Initialize the gina dll
|
|
//
|
|
|
|
if (!InitializeGinaDll(pGlobals))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// So long as action is none, loop here:
|
|
//
|
|
|
|
while (WlxResult == WLX_SAS_ACTION_NONE)
|
|
{
|
|
DealWithAutochkLogs();
|
|
|
|
//
|
|
// If no one is logged on, switch to the display state, and call
|
|
// the gina to display a message. This is structured this way so
|
|
// that a gina can call us immediately during Initialize, and we
|
|
// can fall into the loop in the correct state.
|
|
//
|
|
if (pGlobals->WinlogonState == Winsta_NoOne)
|
|
{
|
|
pGlobals->WinlogonState = Winsta_NoOne_Display;
|
|
DebugLog((DEB_TRACE_STATE, "Setting state to %s\n",
|
|
GetState(Winsta_NoOne_Display)));
|
|
#if DBG
|
|
if (TEST_FLAG(GinaBreakFlags, BREAK_DISPLAY))
|
|
{
|
|
DebugLog((DEB_TRACE, "About to call WlxDisplaySASNotice(%x)\n",
|
|
pGina->pGinaContext));
|
|
DebugBreak();
|
|
}
|
|
#endif
|
|
WlxSetTimeout(pGlobals, TIMEOUT_NONE);
|
|
|
|
pGina->pWlxDisplaySASNotice(pGina->pGinaContext);
|
|
|
|
//
|
|
// If we got a user logoff notify, that means that WE HAVE ALREADY
|
|
// SHUT DOWN. A remote shutdown has taken place, and it has been
|
|
// started by sysshut.c.
|
|
//
|
|
if (pGlobals->SasType == WLX_SAS_TYPE_USER_LOGOFF)
|
|
{
|
|
//
|
|
// We are *done*
|
|
//
|
|
|
|
DebugLog(( DEB_TRACE_STATE, "Received Logoff, setting state to %s\n",
|
|
GetState(Winsta_Shutdown) ));
|
|
|
|
pGlobals->WinlogonState = Winsta_Shutdown;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we got a time out,
|
|
//
|
|
if (pGlobals->SasType == WLX_SAS_TYPE_SCRNSVR_TIMEOUT)
|
|
{
|
|
//
|
|
// run the screen saver
|
|
//
|
|
|
|
ScreenSaverResult = DoScreenSaver( pGlobals, FALSE );
|
|
|
|
if (ScreenSaverResult < 0)
|
|
{
|
|
//
|
|
// This means that a SAS other than activity cancelled
|
|
// the screen saver, such as a GINA specific one.
|
|
// In this case, drop on through to the logonattempt,
|
|
// since the current sas is in pGlobals.
|
|
//
|
|
|
|
NOTHING ;
|
|
|
|
}
|
|
else
|
|
{
|
|
if ( ( (ScreenSaverResult == 0) &&
|
|
(pGlobals->SasType == WLX_SAS_TYPE_USER_LOGOFF) ) ||
|
|
(ScreenSaverResult == WLX_SAS_ACTION_LOGOFF) )
|
|
{
|
|
//
|
|
// Shutdown during the screen saver.
|
|
//
|
|
|
|
DebugLog(( DEB_TRACE_STATE, "Received Logoff during screensaver, setting state to %s\n",
|
|
GetState(Winsta_Shutdown) ));
|
|
|
|
pGlobals->WinlogonState = Winsta_Shutdown;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// And start the loop over again.
|
|
//
|
|
WlxResult = WLX_SAS_ACTION_NONE;
|
|
|
|
//
|
|
// Remember, we're in Winsta_NoOne_Display right now, so we
|
|
// reset this so that we'll drop back into this at the top of
|
|
// the loop.
|
|
//
|
|
|
|
DebugLog((DEB_TRACE_STATE, "Resetting to %s\n",
|
|
GetState(Winsta_NoOne) ));
|
|
|
|
pGlobals->WinlogonState = Winsta_NoOne;
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If we got a user logoff notify, that means that WE HAVE ALREADY
|
|
// SHUT DOWN. A remote shutdown has taken place, and it has been
|
|
// started by sysshut.c.
|
|
//
|
|
if (pGlobals->SasType == WLX_SAS_TYPE_USER_LOGOFF)
|
|
{
|
|
//
|
|
// We are *done*
|
|
//
|
|
DebugLog(( DEB_TRACE_STATE, "Received Logoff during no-one screensaver, setting state to %s\n",
|
|
GetState(Winsta_Shutdown) ));
|
|
|
|
pGlobals->WinlogonState = Winsta_Shutdown;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
WlxResult = LogonAttempt(pGlobals);
|
|
|
|
if (WlxResult == WLX_SAS_ACTION_NONE)
|
|
{
|
|
//
|
|
// If we got a user logoff notify, that means that WE HAVE ALREADY
|
|
// SHUT DOWN. A remote shutdown has taken place, and it has been
|
|
// started by sysshut.c.
|
|
//
|
|
if (pGlobals->SasType == WLX_SAS_TYPE_USER_LOGOFF)
|
|
{
|
|
//
|
|
// We are *done*
|
|
//
|
|
|
|
DebugLog(( DEB_TRACE_STATE, "Got logoff during logon, setting to %s\n",
|
|
GetState(Winsta_Shutdown) ));
|
|
|
|
pGlobals->WinlogonState = Winsta_Shutdown;
|
|
|
|
break;
|
|
}
|
|
//
|
|
// If we got a time out (meaning a screensaver timeout
|
|
// occurred during the logon prompt, then the prompt should be dead,
|
|
// but we'll hit here
|
|
//
|
|
if (pGlobals->SasType == WLX_SAS_TYPE_SCRNSVR_TIMEOUT)
|
|
{
|
|
//
|
|
// run the screen saver
|
|
//
|
|
ScreenSaverResult = DoScreenSaver(pGlobals, FALSE);
|
|
|
|
if (ScreenSaverResult < 0)
|
|
{
|
|
//
|
|
// This means that a SAS other than activity cancelled
|
|
// the screen saver, such as a GINA specific one.
|
|
// In this case, drop on through to the logonattempt,
|
|
// since the current sas is in pGlobals.
|
|
//
|
|
|
|
NOTHING ;
|
|
|
|
}
|
|
else
|
|
{
|
|
if ( (ScreenSaverResult == 0) &&
|
|
(pGlobals->SasType == WLX_SAS_TYPE_USER_LOGOFF) )
|
|
{
|
|
//
|
|
// Shutdown during the screen saver.
|
|
//
|
|
|
|
DebugLog(( DEB_TRACE_STATE, "Received Logoff during screensaver, setting state to %s\n",
|
|
GetState(Winsta_Shutdown) ));
|
|
|
|
pGlobals->WinlogonState = Winsta_Shutdown;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// We're already at WlxResult == NONE, and State == NoOne,
|
|
// so we can just continue
|
|
//
|
|
|
|
}
|
|
|
|
//
|
|
// Make sure that we're back to NoOne:
|
|
//
|
|
|
|
pGlobals->WinlogonState = Winsta_NoOne;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (IsShutdownReturn(WlxResult))
|
|
{
|
|
pGlobals->LastGinaRet = WlxResult;
|
|
DebugLog((DEB_TRACE_STATE, "Setting state to %d (%s)\n",
|
|
Winsta_WaitForShutdown, GetState(Winsta_WaitForShutdown)));
|
|
pGlobals->WinlogonState = Winsta_WaitForShutdown;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Because profile or something else could have gone wrong, the gina
|
|
// could have returned LOGON, but it was changed to LOGOFF in
|
|
// LogonAttempt(). In that case, we don't try and start the shell,
|
|
// we just go straight to logoff processing.
|
|
//
|
|
|
|
if (WlxResult == WLX_SAS_ACTION_LOGON)
|
|
{
|
|
if (DoStartShell(pGlobals))
|
|
{
|
|
WlxResult = BlockWaitForUserAction(pGlobals);
|
|
|
|
if (WlxResult == WLX_SAS_ACTION_FORCE_LOGOFF)
|
|
{
|
|
WaitForForceLogoff(NULL, pGlobals);
|
|
|
|
WlxResult = WLX_SAS_ACTION_LOGOFF;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WlxResult = WLX_SAS_ACTION_LOGOFF;
|
|
}
|
|
}
|
|
|
|
EnableSasMessages(NULL);
|
|
|
|
SetActiveDesktop(&pGlobals->WindowStation, Desktop_Winlogon);
|
|
|
|
if (pGlobals->WinlogonState == Winsta_Shutdown)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Logoff(pGlobals, WlxResult);
|
|
|
|
SecurityChangeUser(pGlobals, NULL, NULL, pGlobals->WinlogonSid, FALSE);
|
|
|
|
if (WlxResult == WLX_SAS_ACTION_LOGOFF)
|
|
{
|
|
pGlobals->WinlogonState = Winsta_NoOne;
|
|
DebugLog((DEB_TRACE, "WlxResult was logoff, so beginning loop again\n"));
|
|
DebugLog((DEB_TRACE_STATE, "State set to %s\n", GetState(Winsta_NoOne)));
|
|
WlxResult = WLX_SAS_ACTION_NONE;
|
|
}
|
|
|
|
//
|
|
// Notify the GINA that the user is logged off
|
|
//
|
|
|
|
#if DBG
|
|
if (TEST_FLAG(GinaBreakFlags, BREAK_LOGOFF))
|
|
{
|
|
DebugLog((DEB_TRACE, "About to call WlxLogoff(%x)\n", pGina->pGinaContext));
|
|
DebugBreak();
|
|
}
|
|
#endif
|
|
pGina->pWlxLogoff(pGina->pGinaContext);
|
|
|
|
//
|
|
// Toggle the winsta lock on and off. This clears the openlock, and
|
|
// sets the switchlock, allowing services to start, but no one can
|
|
// switch the active desktop.
|
|
//
|
|
|
|
UnlockWindowStation(pGlobals->WindowStation.hwinsta);
|
|
LockWindowStation(pGlobals->WindowStation.hwinsta);
|
|
|
|
#if DBG
|
|
if ((WlxResult == WLX_SAS_ACTION_NONE) ||
|
|
(WlxResult == WLX_SAS_ACTION_LOGON) ||
|
|
(WlxResult == WLX_SAS_ACTION_SHUTDOWN) ||
|
|
(WlxResult == WLX_SAS_ACTION_SHUTDOWN_REBOOT) ||
|
|
(WlxResult == WLX_SAS_ACTION_SHUTDOWN_POWER_OFF) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
DebugLog((DEB_TRACE, "WlxResult not acceptible value: %d\n", WlxResult));
|
|
DebugLog((DEB_TRACE, "Resetting to WLX_SAS_ACTION_NONE\n"));
|
|
WlxResult = WLX_SAS_ACTION_NONE;
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: LogoffFlagsToWlxCode
|
|
//
|
|
// Synopsis: Translates a winsrv USER_LOGOFF message flags to a
|
|
// wlx return code.
|
|
//
|
|
// Arguments: [Flags] --
|
|
//
|
|
// History: 10-17-94 RichardW Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD
|
|
LogoffFlagsToWlxCode(DWORD Flags)
|
|
{
|
|
if (Flags & EWX_POWEROFF)
|
|
{
|
|
return(WLX_SAS_ACTION_SHUTDOWN_POWER_OFF);
|
|
}
|
|
if (Flags & EWX_REBOOT)
|
|
{
|
|
return(WLX_SAS_ACTION_SHUTDOWN_REBOOT);
|
|
}
|
|
if (Flags & EWX_SHUTDOWN)
|
|
{
|
|
return(WLX_SAS_ACTION_SHUTDOWN);
|
|
}
|
|
|
|
return(WLX_SAS_ACTION_LOGOFF);
|
|
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: WinsrvNotify
|
|
//
|
|
// Synopsis: Handles when winsrv talks to us
|
|
//
|
|
// Arguments: [pGlobals] --
|
|
// [SasType] --
|
|
//
|
|
// Algorithm:
|
|
//
|
|
// History: 10-17-94 RichardW Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
WinsrvNotify(
|
|
PGLOBALS pGlobals,
|
|
DWORD SasType)
|
|
{
|
|
DWORD RealFlags;
|
|
DWORD LogoffResult;
|
|
WinstaState PriorState;
|
|
//
|
|
// If the caller isn't system, and no-one is logged on, discard this message.
|
|
//
|
|
|
|
if (!(pGlobals->LogoffFlags & EWX_SYSTEM_CALLER) && !pGlobals->UserLoggedOn)
|
|
{
|
|
DebugLog((DEB_TRACE, "Discarding notice from winsrv!\n"));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If this indicates that winlogon initiated this message (by calling
|
|
// InitiateLogoff() somewhere else, or we're in a wait state, then pass
|
|
// the message along. This is what will kill our LoggedOnDlg.
|
|
//
|
|
|
|
if ((pGlobals->LogoffFlags & EWX_WINLOGON_CALLER) ||
|
|
(pGlobals->WinlogonState == Winsta_WaitForLogoff) ||
|
|
(pGlobals->WinlogonState == Winsta_WaitForShutdown) ||
|
|
(pGlobals->WinlogonState == Winsta_InShutdownDlg) )
|
|
{
|
|
SASRouter(pGlobals, SasType);
|
|
RealFlags = RealFlagsFromStoredFlags(pGlobals->LogoffFlags);
|
|
pGlobals->LastGinaRet = LogoffFlagsToWlxCode(RealFlags);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Well, this means that the user has called ExitWindowsEx(), and winsrv
|
|
// has passed the ball to us. We have to turn around and ask the gina if
|
|
// logoff is ok. This is convenient for some security architectures. I
|
|
// guess.
|
|
//
|
|
|
|
#if DBG
|
|
if (TEST_FLAG(GinaBreakFlags, BREAK_ISLOGOFFOK))
|
|
{
|
|
DebugLog((DEB_TRACE, "About to call WlxIsLogoffOk(%#x)\n", pGlobals->pGina->pGinaContext));
|
|
DebugBreak();
|
|
}
|
|
#endif
|
|
if (!pGlobals->pGina->pWlxIsLogoffOk(pGlobals->pGina->pGinaContext))
|
|
{
|
|
DebugLog((DEB_TRACE, "Gina said no logoff...\n"));
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Well, if we're not in a wait state, then initiate logoff and possibly
|
|
// shutdown. Convoluted, right?
|
|
//
|
|
|
|
if ((pGlobals->WinlogonState != Winsta_WaitForLogoff) &&
|
|
(pGlobals->WinlogonState != Winsta_WaitForShutdown) &&
|
|
(pGlobals->WinlogonState != Winsta_InShutdownDlg) )
|
|
{
|
|
PriorState = pGlobals->WinlogonState;
|
|
pGlobals->WinlogonState = Winsta_WaitForLogoff;
|
|
DebugLog((DEB_TRACE_STATE, "WinsrvNotify: Setting state to %s\n",
|
|
GetState(Winsta_WaitForLogoff)));
|
|
|
|
pGlobals->LastGinaRet = LogoffFlagsToWlxCode(pGlobals->LogoffFlags);
|
|
DebugLog((DEB_TRACE, "Setting lastginaret to %s\n",
|
|
WlxName(pGlobals->LastGinaRet)));
|
|
|
|
LogoffResult = InitiateLogoff( pGlobals,
|
|
(pGlobals->LogoffFlags & EWX_FORCE) |
|
|
StoredFlagsFromRealFlags(pGlobals->LogoffFlags)
|
|
);
|
|
|
|
if (LogoffResult == DLG_FAILURE)
|
|
{
|
|
DebugLog((DEB_TRACE, "Logoff refused, resetting\n"));
|
|
pGlobals->WinlogonState = PriorState;
|
|
DebugLog((DEB_TRACE_STATE, "WinsrvNotify: resetting state back to %s\n",
|
|
GetState(PriorState)));
|
|
}
|
|
}
|
|
|
|
}
|