|
|
/**************************** Module Header ********************************\
* Module Name: exitwin.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * History: * 07-23-92 ScottLu Created. \***************************************************************************/
#include "precomp.h"
#pragma hdrstop
#define OPTIONMASK (EWX_SHUTDOWN | EWX_REBOOT | EWX_FORCE)
/*
* Globals local to this file only */ PWINDOWSTATION gpwinstaLogoff; DWORD gdwLocks; DWORD gdwShutdownFlags; HANDLE gpidEndSession;
#ifdef PRERELEASE
DWORD gdwllParamCopy, gdwStatusCopy, gdwFlagsCopy; BOOL gfNotifiedCopy; #endif // PRERELEASE
/*
* Called by ExitWindowsEx() to check whether the thread is permitted to logoff. * If it is, and this is WinLogon calling, then also save any of the user's * setting that have not yet been stored in the profile. */ BOOL PrepareForLogoff( UINT uFlags) { PTHREADINFO ptiCurrent = PtiCurrent();
CheckCritIn();
if (ptiCurrent->TIF_flags & TIF_RESTRICTED) { PW32JOB pW32Job;
pW32Job = ptiCurrent->ppi->pW32Job;
UserAssert(pW32Job != NULL);
if (pW32Job->restrictions & JOB_OBJECT_UILIMIT_EXITWINDOWS) { // Not permitted to ExitWindows.
return FALSE; } }
/*
* There are no restrictions, or the restriction do not deny shutdown: * The caller is about to ExitWindowsEx via CSR, so save the volatile * elements of the User preferences in their profile */ if (PsGetThreadProcessId(ptiCurrent->pEThread) == gpidLogon) { /*
* Save the current user's NumLock state */ TL tlName; PUNICODE_STRING pProfileUserName = CreateProfileUserName(&tlName); RegisterPerUserKeyboardIndicators(pProfileUserName); FreeProfileUserName(pProfileUserName, &tlName); }
return TRUE; UNREFERENCED_PARAMETER(uFlags); }
BOOL NotifyLogon( PWINDOWSTATION pwinsta, PLUID pluidCaller, DWORD dwFlags, NTSTATUS StatusCode) { BOOL fNotified = FALSE; DWORD dwllParam; DWORD dwStatus;
if (!(dwFlags & EWX_NONOTIFY)) {
if (dwFlags & EWX_CANCELED) { dwllParam = LOGON_LOGOFFCANCELED; dwStatus = StatusCode; } else { dwllParam = LOGON_LOGOFF; dwStatus = dwFlags; }
if (dwFlags & EWX_SHUTDOWN) { /*
* Post the message to the global logon notify window */ if (gspwndLogonNotify != NULL) { _PostMessage(gspwndLogonNotify, WM_LOGONNOTIFY, dwllParam, (LONG)dwStatus); fNotified = TRUE; } } else { if (gspwndLogonNotify != NULL && (RtlEqualLuid(&pwinsta->luidUser, pluidCaller) || RtlEqualLuid(&luidSystem, pluidCaller))) { _PostMessage(gspwndLogonNotify, WM_LOGONNOTIFY, dwllParam, (LONG)dwStatus); fNotified = TRUE; } } }
#ifdef PRERELEASE
/*
* Remember what these were for debugging purposes. */ gdwllParamCopy = dwllParam; gdwFlagsCopy = dwFlags; gdwStatusCopy = dwStatus; gfNotifiedCopy = fNotified; #endif // PRERELEASE
return fNotified; }
NTSTATUS InitiateShutdown( PETHREAD Thread, PULONG lpdwFlags) { static PRIVILEGE_SET psShutdown = { 1, PRIVILEGE_SET_ALL_NECESSARY, { SE_SHUTDOWN_PRIVILEGE, 0 } }; PEPROCESS Process; LUID luidCaller; PPROCESSINFO ppi; PWINDOWSTATION pwinsta; HWINSTA hwinsta; PTHREADINFO ptiClient; NTSTATUS Status; DWORD dwFlags;
/*
* Find out the callers sid. Only want to shutdown processes in the * callers sid. */ Process = PsGetThreadProcess(Thread); ptiClient = PtiFromThread(Thread); Status = GetProcessLuid(Thread, &luidCaller);
if (!NT_SUCCESS(Status)) { return Status; }
/*
* Set the system flag if the caller is a system process. * Winlogon uses this to determine in which context to perform * a shutdown operation. */ dwFlags = *lpdwFlags; if (RtlEqualLuid(&luidCaller, &luidSystem)) { dwFlags |= EWX_SYSTEM_CALLER; } else { dwFlags &= ~EWX_SYSTEM_CALLER; }
/*
* Find a windowstation. If the process does not have one * assigned, use the standard one. */ ppi = PpiFromProcess(Process); if (ppi == NULL) { /*
* We ran into a case where the thread was terminated and had already * been cleaned up by USER. Thus, the ppi and ptiClient was NULL. */ return STATUS_INVALID_HANDLE; } pwinsta = ppi->rpwinsta; hwinsta = ppi->hwinsta; /*
* If we're not being called by Winlogon, validate the call and * notify the logon process to do the actual shutdown. */ if (PsGetThreadProcessId(Thread) != gpidLogon) { dwFlags &= ~EWX_WINLOGON_CALLER; *lpdwFlags = dwFlags;
if (pwinsta == NULL) { #ifndef LATER
return STATUS_INVALID_HANDLE; #else
hwinsta = ppi->pOpenObjectTable[HI_WINDOWSTATION].h; if (hwinsta == NULL) { return STATUS_INVALID_HANDLE; } pwinsta = (PWINDOWSTATION)ppi->pOpenObjectTable[HI_WINDOWSTATION].phead; #endif
}
/*
* Check security first - does this thread have access? */ if (!RtlAreAllAccessesGranted(ppi->amwinsta, WINSTA_EXITWINDOWS)) { return STATUS_ACCESS_DENIED; }
/*
* If the client requested shutdown, reboot, or poweroff they must have * the shutdown privilege. */ if (dwFlags & EWX_SHUTDOWN) { if (!IsPrivileged(&psShutdown) ) { return STATUS_PRIVILEGE_NOT_HELD; } } else {
/*
* If this is a non-IO windowstation and we are not shutting down, * fail the call. */ if (pwinsta->dwWSF_Flags & WSF_NOIO) { return STATUS_INVALID_DEVICE_REQUEST; } } }
/*
* Is there a shutdown already in progress? */ if (gdwThreadEndSession != 0) { DWORD dwNew;
/*
* If the current shutdown in another sid and is not being done by * winlogon, override it. */ if (!RtlEqualLuid(&luidCaller, &gpwinstaLogoff->luidEndSession) && (gpidEndSession != gpidLogon)) { return STATUS_RETRY; }
/*
* Calculate new flags */ dwNew = dwFlags & OPTIONMASK & (~gdwShutdownFlags);
/*
* Should we override the other shutdown? Make sure * winlogon does not recurse. */ if (dwNew && HandleToUlong(PsGetCurrentThreadId()) != gdwThreadEndSession) { /*
* Only one windowstation can be logged off at a time. */ if (!(dwFlags & EWX_SHUTDOWN) && pwinsta != gpwinstaLogoff) { return STATUS_DEVICE_BUSY; } /* Bug# 453872
* Since we are about to fail this call. Do not change gdwShutdownFlags * Later when we notify winlogon in EndShtdown, if we changed gdwShutdownFlags * and the call does not have EWX_WINLOGON_CALLER, winlogon will abort the call * to take care of the case when an application keeps calling ExitWindows. * [msadek- 08/08/2001] */ #if 0
/*
* Set the new flags */ gdwShutdownFlags = dwFlags; #endif
if (dwNew & EWX_FORCE) { return STATUS_RETRY; } else { return STATUS_PENDING; } } else { /*
* Don't override */ return STATUS_PENDING; } }
/*
* If the caller is not winlogon, signal winlogon to start * the real shutdown. */ if (PsGetThreadProcessId(Thread) != gpidLogon) { if (dwFlags & EWX_NOTIFY) { if (ptiClient && ptiClient->TIF_flags & TIF_16BIT) gptiShutdownNotify = ptiClient; dwFlags &= ~EWX_NOTIFY; *lpdwFlags = dwFlags; }
if (NotifyLogon(pwinsta, &luidCaller, dwFlags, STATUS_SUCCESS)) return STATUS_PENDING; else if (ptiClient && ptiClient->cWindows) return STATUS_CANT_WAIT; }
/*
* Mark this thread as the one that is currently processing * exit windows, and set the global saying someone is exiting */ dwFlags |= EWX_WINLOGON_CALLER; *lpdwFlags = dwFlags; gdwShutdownFlags = dwFlags;
gdwThreadEndSession = HandleToUlong(PsGetCurrentThreadId()); SETSYSMETBOOL(SHUTTINGDOWN, TRUE); gpidEndSession = PsGetCurrentThreadProcessId(); gpwinstaLogoff = pwinsta; pwinsta->luidEndSession = luidCaller;
/*
* Lock the windowstation to prevent apps from starting * while we're doing shutdown processing. */ gdwLocks = pwinsta->dwWSF_Flags & (WSF_SWITCHLOCK | WSF_OPENLOCK); pwinsta->dwWSF_Flags |= (WSF_OPENLOCK | WSF_SHUTDOWN);
/*
* Set the flag WSF_REALSHUTDOWN if we are not doing just a * logoff */ if (dwFlags & (EWX_WINLOGON_OLD_SHUTDOWN | EWX_WINLOGON_OLD_REBOOT | EWX_SHUTDOWN | EWX_REBOOT)) {
pwinsta->dwWSF_Flags |= WSF_REALSHUTDOWN; }
return STATUS_SUCCESS; }
NTSTATUS EndShutdown( PETHREAD Thread, NTSTATUS StatusShutdown) { PWINDOWSTATION pwinsta = gpwinstaLogoff; PDESKTOP pdesk; LUID luidCaller; UserAssert(gpwinstaLogoff);
gpwinstaLogoff = NULL; gpidEndSession = NULL; gdwThreadEndSession = 0; SETSYSMETBOOL(SHUTTINGDOWN, FALSE); pwinsta->dwWSF_Flags &= ~WSF_SHUTDOWN;
if (!NT_SUCCESS(GetProcessLuid(Thread, &luidCaller))) { luidCaller = RtlConvertUlongToLuid(0); // null luid
}
if (!NT_SUCCESS(StatusShutdown)) {
/*
* We need to notify the process that called ExitWindows that * the logoff was aborted. */ if (gptiShutdownNotify) { _PostThreadMessage(gptiShutdownNotify, WM_ENDSESSION, FALSE, 0); gptiShutdownNotify = NULL; }
/*
* Reset the windowstation lock flags so apps can start * again. */ pwinsta->dwWSF_Flags = (pwinsta->dwWSF_Flags & ~WSF_OPENLOCK) | gdwLocks;
/*
* Bug 294204 - joejo * Tell winlogon that we we cancelled shutdown/logoff. */ NotifyLogon(pwinsta, &luidCaller, gdwShutdownFlags | EWX_CANCELED, StatusShutdown);
return STATUS_SUCCESS; }
gptiShutdownNotify = NULL;
/*
* If logoff is occuring for the user set by winlogon, perform * the normal logoff cleanup. Otherwise, clear the open lock * and continue. */ if (((pwinsta->luidUser.LowPart != 0) || (pwinsta->luidUser.HighPart != 0)) && RtlEqualLuid(&pwinsta->luidUser, &luidCaller)) {
/*
* Zero out the free blocks in all desktop heaps. */ for (pdesk = pwinsta->rpdeskList; pdesk != NULL; pdesk = pdesk->rpdeskNext) { RtlZeroHeap(Win32HeapGetHandle(pdesk->pheapDesktop), 0); }
/*
* Logoff/shutdown was successful. In case this is a logoff, remove * everything from the clipboard so the next logged on user can't get * at this stuff. */ ForceEmptyClipboard(pwinsta);
/*
* Destroy all non-pinned atoms in the global atom table. User can't * create pinned atoms. Currently only the OLE atoms are pinned. */ RtlEmptyAtomTable(pwinsta->pGlobalAtomTable, FALSE);
// this code path is hit only on logoff and also on shutdown
// We do not want to unload fonts twice when we attempt shutdown
// so we mark that the fonts have been unloaded at a logoff time
if (TEST_PUDF(PUDF_FONTSARELOADED)) { LeaveCrit(); GreRemoveAllButPermanentFonts(); EnterCrit(); CLEAR_PUDF(PUDF_FONTSARELOADED); } } else { pwinsta->dwWSF_Flags &= ~WSF_OPENLOCK; }
/*
* Tell winlogon that we successfully shutdown/logged off. */ NotifyLogon(pwinsta, &luidCaller, gdwShutdownFlags, STATUS_SUCCESS);
return STATUS_SUCCESS; }
/***************************************************************************\
* xxxClientShutdown2 * * Called by xxxClientShutdown \***************************************************************************/
LONG xxxClientShutdown2( PBWL pbwl, UINT msg, WPARAM wParam) { HWND *phwnd; PWND pwnd; TL tlpwnd; BOOL fEnd; PTHREADINFO ptiCurrent = PtiCurrent(); BOOL fDestroyTimers; LPARAM lParam;
/*
* Make sure we don't send this window any more WM_TIMER * messages if the session is ending. This was causing * AfterDark to fault when it freed some memory on the * WM_ENDSESSION and then tried to reference it on the * WM_TIMER. * LATER GerardoB: Do we still need to do this?? * Do this horrible thing only if the process is in the * context being logged off. * Perhaps someday we should post a WM_CLOSE so the app * gets a better chance to clean up (if this process is in * the context being logged off, winsrv is going to call * TerminateProcess soon after this). */ fDestroyTimers = (wParam & WMCS_EXIT) && (wParam & WMCS_CONTEXTLOGOFF);
/*
* fLogOff and fEndSession parameters (WM_ENDSESSION only) */ lParam = wParam & ENDSESSION_LOGOFF; wParam &= WMCS_EXIT;
/*
* Now enumerate these windows and send the WM_QUERYENDSESSION or * WM_ENDSESSION messages. */ for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) { if ((pwnd = RevalidateHwnd(*phwnd)) == NULL) continue;
ThreadLockAlways(pwnd, &tlpwnd);
/*
* Send the message. */ switch (msg) { case WM_QUERYENDSESSION:
/*
* Windows does not send the WM_QUERYENDSESSION to the app * that called ExitWindows */ if (ptiCurrent == gptiShutdownNotify) { fEnd = TRUE; } else { fEnd = (xxxSendMessage(pwnd, WM_QUERYENDSESSION, FALSE, lParam) != 0); if (!fEnd) { RIPMSG2(RIP_WARNING, "xxxClientShutdown2: pwnd:%p canceled shutdown. lParam:%p", pwnd, lParam); } } break;
case WM_ENDSESSION: xxxSendMessage(pwnd, WM_ENDSESSION, wParam, lParam); fEnd = TRUE;
if (fDestroyTimers) { DestroyWindowsTimers(pwnd); }
break; }
ThreadUnlock(&tlpwnd);
if (!fEnd) return WMCSR_CANCEL; }
return WMCSR_ALLOWSHUTDOWN; } /***************************************************************************\
* xxxClientShutdown * * This is the processing that occurs when an application receives a * WM_CLIENTSHUTDOWN message. * * 10-01-92 ScottLu Created. \***************************************************************************/ LONG xxxClientShutdown( PWND pwnd, WPARAM wParam) { PBWL pbwl; PTHREADINFO ptiT; LONG lRet;
/*
* Build a list of windows first. */ ptiT = GETPTI(pwnd);
if ((pbwl = BuildHwndList(ptiT->rpdesk->pDeskInfo->spwnd->spwndChild, BWL_ENUMLIST, ptiT)) == NULL) { /*
* Can't allocate memory to notify this thread's windows of shutdown. * Can't do more than kill the app */ return WMCSR_ALLOWSHUTDOWN; }
if (wParam & WMCS_QUERYEND) { lRet = xxxClientShutdown2(pbwl, WM_QUERYENDSESSION, wParam); } else { xxxClientShutdown2(pbwl, WM_ENDSESSION, wParam); lRet = WMCSR_DONE; }
FreeHwndList(pbwl); return lRet; }
/***************************************************************************\
* xxxRegisterUserHungAppHandlers * * This routine simply records the WOW callback address for notification of * "hung" wow apps. * * History: * 01-Apr-1992 jonpa Created. * Added saving and duping of wowexc event handle \***************************************************************************/
BOOL xxxRegisterUserHungAppHandlers( PFNW32ET pfnW32EndTask, HANDLE hEventWowExec) { BOOL bRetVal; PPROCESSINFO ppi; PWOWPROCESSINFO pwpi; ULONG ProcessInfo; NTSTATUS Status;
//
// Check the Target Process to see if this is a 16-bit process
//
Status = ZwQueryInformationProcess( NtCurrentProcess(), ProcessWx86Information, &ProcessInfo, sizeof(ProcessInfo), NULL );
if (!NT_SUCCESS(Status) || ProcessInfo == 0) { return FALSE; }
//
// Allocate the per wow process info stuff
// ensuring the memory is Zero init.
//
pwpi = (PWOWPROCESSINFO) UserAllocPoolWithQuotaZInit( sizeof(WOWPROCESSINFO), TAG_WOWPROCESSINFO);
if (!pwpi) return FALSE;
//
// Reference the WowExec event for kernel access
//
bRetVal = NT_SUCCESS(ObReferenceObjectByHandle( hEventWowExec, EVENT_ALL_ACCESS, *ExEventObjectType, UserMode, &pwpi->pEventWowExec, NULL ));
//
// if sucess then intialize the pwpi, ppi structs
// else free allocated memory
//
if (bRetVal) { pwpi->hEventWowExecClient = hEventWowExec; pwpi->lpfnWowExitTask = pfnW32EndTask; ppi = PpiCurrent(); ppi->pwpi = pwpi;
// add to the list, order doesn't matter
pwpi->pwpiNext = gpwpiFirstWow; gpwpiFirstWow = pwpi;
} else { UserFreePool(pwpi); }
return bRetVal; }
|