|
|
// --------------------------------------------------------------------------
// Module Name: WaitInteractiveReady.cpp
//
// Copyright (c) 2001, Microsoft Corporation
//
// Class to handle waiting on the shell signal the desktop switch.
//
// History: 2001-01-15 vtan created
// --------------------------------------------------------------------------
#include "StandardHeader.h"
#include "WaitInteractiveReady.h"
#include <ginaipc.h>
#include <msginaexports.h>
#include "Impersonation.h"
#include "StatusCode.h"
// --------------------------------------------------------------------------
// CWaitInteractiveReady::s_pWlxContext
// CWaitInteractiveReady::s_hWait
// CWaitInteractiveReady::s_hEvent
// CWaitInteractiveReady::s_hEventShellReady
// CWaitInteractiveReady::s_szEventName
//
// Purpose: Static member variables.
//
// History: 2001-01-15 vtan created
// --------------------------------------------------------------------------
HANDLE CWaitInteractiveReady::s_hWait = NULL; CWaitInteractiveReady* CWaitInteractiveReady::s_pWaitInteractiveReady = NULL; HANDLE CWaitInteractiveReady::s_hEventShellReady = NULL; const TCHAR CWaitInteractiveReady::s_szEventName[] = TEXT("msgina: ShellReadyEvent");
// --------------------------------------------------------------------------
// CWaitInteractiveReady::CWaitInteractiveReady
//
// Arguments: pWlxContext = PGLOBALS struct for msgina.
//
// Returns: <none>
//
// Purpose: Private constructor for this class. Create a synchronization
// event for callback state determination.
//
// History: 2001-07-17 vtan created
// --------------------------------------------------------------------------
CWaitInteractiveReady::CWaitInteractiveReady (void *pWlxContext) : _pWlxContext(pWlxContext), _hEvent(CreateEvent(NULL, TRUE, FALSE, NULL))
{ }
// --------------------------------------------------------------------------
// CWaitInteractiveReady::~CWaitInteractiveReady
//
// Arguments: <none>
//
// Returns: <none>
//
// Purpose: Destructor. Clears member variables.
//
// History: 2001-07-17 vtan created
// --------------------------------------------------------------------------
CWaitInteractiveReady::~CWaitInteractiveReady (void)
{ ReleaseHandle(_hEvent); _pWlxContext = NULL; }
// --------------------------------------------------------------------------
// CWaitInteractiveReady::Create
//
// Arguments: pWlxContext = PGLOBALS struct for msgina.
//
// Returns: NTSTATUS
//
// Purpose: Creates resources required to manage switching desktops when
// the shell signals the interactive ready event. This allows
// the shell to be brought up in an interactive state.
//
// History: 2001-01-15 vtan created
// --------------------------------------------------------------------------
NTSTATUS CWaitInteractiveReady::Create (void *pWlxContext)
{ NTSTATUS status; HANDLE hToken;
ASSERTMSG(s_hWait == NULL, "Wait already registered in CWaitInteractiveReady::Start"); ASSERTMSG(s_hEventShellReady == NULL, "Named event already exists in CWaitInteractiveReady::Start"); hToken = _Gina_GetUserToken(pWlxContext); if (hToken != NULL) { CImpersonation impersonation(hToken);
if (impersonation.IsImpersonating()) { s_hEventShellReady = CreateEvent(NULL, TRUE, FALSE, s_szEventName); if (s_hEventShellReady != NULL) { status = STATUS_SUCCESS; } else { status = CStatusCode::StatusCodeOfLastError(); TSTATUS(ReleaseEvent()); } } else { status = STATUS_BAD_IMPERSONATION_LEVEL; } } else { status = STATUS_NO_TOKEN; } return(status); }
// --------------------------------------------------------------------------
// CWaitInteractiveReady::Register
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Checks the state of the event being waited on. It's possible
// that explorer may have already signaled this event before this
// code is executed. If the event is signaled then CB_ShellReady
// has already been called.
//
// History: 2001-07-16 vtan created
// --------------------------------------------------------------------------
NTSTATUS CWaitInteractiveReady::Register (void *pWlxContext)
{ NTSTATUS status;
ASSERTMSG(s_hWait == NULL, "Wait already registered in CWaitInteractiveReady::Check");
// Check and Stop should not be called from any thread other than
// the main thread of winlogon. It's called in only a few places.
// Firstly check the named event (msgina: ShellReadyEvent).
if (s_hEventShellReady != NULL) {
// If it exists then check to see if it's signaled.
if (WaitForSingleObject(s_hEventShellReady, 0) == WAIT_OBJECT_0) {
// If it's signaled then release the resources and return
// a failure code (force it down the classic UI path).
TSTATUS(ReleaseEvent()); status = STATUS_UNSUCCESSFUL; } else { CWaitInteractiveReady *pWaitInteractiveReady;
pWaitInteractiveReady = new CWaitInteractiveReady(pWlxContext); if (pWaitInteractiveReady != NULL) { if (pWaitInteractiveReady->IsCreated()) {
// Otherwise if it's not signaled then register a wait on
// the named object for 30 seconds.
if (RegisterWaitForSingleObject(&s_hWait, s_hEventShellReady, CB_ShellReady, pWaitInteractiveReady, 30000, WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE) == FALSE) { status = CStatusCode::StatusCodeOfLastError(); delete pWaitInteractiveReady; TSTATUS(ReleaseEvent()); } else { s_pWaitInteractiveReady = pWaitInteractiveReady; status = STATUS_SUCCESS; } } else { delete pWaitInteractiveReady; TSTATUS(ReleaseEvent()); status = STATUS_NO_MEMORY; } } else { TSTATUS(ReleaseEvent()); status = STATUS_NO_MEMORY; } } } else { status = STATUS_UNSUCCESSFUL; } return(status); }
// --------------------------------------------------------------------------
// CWaitInteractiveReady::Cancel
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Removes the wait on the interactive ready object. This is
// done when a user causes a return to welcome. This is
// necessary because if the callback fires AFTER the return to
// welcome we will switch to the user's desktop which violates
// security.
//
// History: 2001-01-15 vtan created
// --------------------------------------------------------------------------
NTSTATUS CWaitInteractiveReady::Cancel (void)
{ HANDLE hWait;
// Grab the global hWait. If somebody beat us to this or it
// didn't exist then there's nothing to do.
hWait = InterlockedExchangePointer(&s_hWait, NULL); if (hWait != NULL) { CWaitInteractiveReady *pThis;
// Grab the s_pWaitInteractiveReady. This is a pointer to the callback
// memory. It will be valid unless the callback has already interlocked
// the variable itself which means the callback has reached the determined
// p0int already anyway and no wait is necessary.
pThis = static_cast<CWaitInteractiveReady*>(InterlockedExchangePointer(reinterpret_cast<void**>(&s_pWaitInteractiveReady), NULL));
// Try to unregister the wait. If this fails then the callback
// is being executed. Wait until the callback reaches a determined
// point (it will signal the internal event). Wait TWO minutes for
// this. We cannot block the main thread of winlogon. If everything
// is working nicely then this will be a no-brainer wait.
if (UnregisterWait(hWait) == FALSE) {
// If the unregister fails then wait if there's a valid event
// to wait on - reasons explained above.
if (pThis != NULL) { (DWORD)WaitForSingleObject(pThis->_hEvent, 120000); } } else {
// Otherwise the wait was successfully unregistered indicating the
// callback is not executing. Release the memory that was allocated
// for it because it's not going to execute now.
if (pThis != NULL) { delete pThis; } } }
// Always release the wait handle. This is valid because if the callback
// is executing and it grabbed the s_hWait then it will be NULL and it will
// also try to release the event handle. If there was no s_hWait then we
// just release the event handle anyway. Otherwise we grabbed the s_hWait
// above and can release the event handle as well.
TSTATUS(ReleaseEvent()); return(STATUS_SUCCESS); }
// --------------------------------------------------------------------------
// CWaitInteractiveReady::IsCreated
//
// Arguments: <none>
//
// Returns: bool
//
// Purpose: Returns whether the object is successfully created.
//
// History: 2001-07-17 vtan created
// --------------------------------------------------------------------------
bool CWaitInteractiveReady::IsCreated (void) const
{ return(_hEvent != NULL); }
// --------------------------------------------------------------------------
// CWaitInteractiveReady::ReleaseEvent
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Resets the static member variables to the uninitialized
// state.
//
// History: 2001-01-15 vtan created
// --------------------------------------------------------------------------
NTSTATUS CWaitInteractiveReady::ReleaseEvent (void)
{ HANDLE h;
h = InterlockedExchangePointer(&s_hEventShellReady, NULL); if (h != NULL) { TBOOL(CloseHandle(h)); } return(STATUS_SUCCESS); }
// --------------------------------------------------------------------------
// CWaitInteractiveReady::CB_ShellReady
//
// Arguments: pParameter = User callback parameter.
// TimerOrWaitFired = Timer or wait fired.
//
// Returns: <none>
//
// Purpose: Invoked when the interactive ready event is signaled by the
// shell. Switch the desktop to the user's desktop.
//
// History: 2001-01-15 vtan created
// --------------------------------------------------------------------------
void CALLBACK CWaitInteractiveReady::CB_ShellReady (void *pParameter, BOOLEAN TimerOrWaitFired)
{ UNREFERENCED_PARAMETER(TimerOrWaitFired);
HANDLE hWait; CWaitInteractiveReady *pThis;
pThis = static_cast<CWaitInteractiveReady*>(pParameter);
// Wrap the desktop manipulation around a scope which saves and restores
// the desktop. _Gina_SwitchDesktopToUser will set the thread's desktop
// to \Default and will NOT restore it. This scoped object will restore it.
if (pThis->_pWlxContext != NULL) { CDesktop desktop;
// Hide the status host. Switch the desktops.
_ShellStatusHostEnd(HOST_END_HIDE); (int)_Gina_SwitchDesktopToUser(pThis->_pWlxContext); }
// Signal the internal event.
TBOOL(SetEvent(pThis->_hEvent));
// Grab the global hWait. If somebody beat us to it then they're trying
// to stop this from happening. They could beat us to it at any time from
// the invokation of the callback to here. That thread will wait for this
// one to signal the internal event. In that case there's no work for this
// thread. The owner of the hWait has to clean up. If this thread gets the
// hWait then unregister the wait and release the resources.
hWait = InterlockedExchangePointer(&s_hWait, NULL); if (hWait != NULL) { (BOOL)UnregisterWait(hWait); TSTATUS(ReleaseEvent()); }
// Interlock the s_pWaitInteractiveReady variable which is also an
// indicator of having reached the determined point in the callback.
(CWaitInteractiveReady*)InterlockedExchangePointer(reinterpret_cast<void**>(&s_pWaitInteractiveReady), NULL);
// Delete our blob of data.
delete pThis; }
|