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.
557 lines
15 KiB
557 lines
15 KiB
/**************************** Module Header ********************************\
|
|
* Module Name: exitwin.c
|
|
*
|
|
* Copyright 1985-92, Microsoft Corporation
|
|
*
|
|
* NT: Logoff user
|
|
* DOS: Exit windows
|
|
*
|
|
* 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;
|
|
|
|
extern PSECURITY_DESCRIPTOR gpsdInitWinSta;
|
|
|
|
|
|
|
|
NTSTATUS
|
|
RtlZeroHeap(
|
|
IN PVOID HeapHandle,
|
|
IN ULONG Flags
|
|
);
|
|
|
|
BOOL NotifyLogon(
|
|
PWINDOWSTATION pwinsta,
|
|
PLUID pluidCaller,
|
|
DWORD dwFlags)
|
|
{
|
|
BOOL fNotified = FALSE;
|
|
PWND pwndWinlogon;
|
|
|
|
if (dwFlags & EWX_SHUTDOWN) {
|
|
for (pwinsta = grpwinstaList; pwinsta != NULL;
|
|
pwinsta = pwinsta->rpwinstaNext) {
|
|
pwndWinlogon = pwinsta->spwndLogonNotify;
|
|
if (pwndWinlogon != NULL) {
|
|
_PostMessage(pwndWinlogon, WM_LOGONNOTIFY, LOGON_LOGOFF,
|
|
(LONG)dwFlags);
|
|
fNotified = TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
LUID luidSystem = SYSTEM_LUID;
|
|
|
|
pwndWinlogon = pwinsta->spwndLogonNotify;
|
|
if (pwndWinlogon != NULL &&
|
|
(RtlEqualLuid(&pwinsta->luidUser, pluidCaller) ||
|
|
RtlEqualLuid(&luidSystem, pluidCaller))) {
|
|
_PostMessage(pwndWinlogon, WM_LOGONNOTIFY, LOGON_LOGOFF,
|
|
(LONG)dwFlags);
|
|
fNotified = TRUE;
|
|
}
|
|
}
|
|
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;
|
|
LUID luidSystem = SYSTEM_LUID;
|
|
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 = THREAD_TO_PROCESS(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 (Thread->Cid.UniqueProcess != 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->dwFlags & WSF_NOIO) {
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Is there a shutdown already in progress?
|
|
*/
|
|
if (dwThreadEndSession != 0) {
|
|
DWORD dwNew;
|
|
|
|
/*
|
|
* Calculate new flags
|
|
*/
|
|
dwNew = dwFlags & OPTIONMASK & (~gdwShutdownFlags);
|
|
|
|
/*
|
|
* Should we override the other shutdown? Make sure
|
|
* winlogon does not recurse.
|
|
*/
|
|
if (dwNew && (DWORD)PsGetCurrentThread()->Cid.UniqueThread !=
|
|
dwThreadEndSession) {
|
|
/*
|
|
* Only one windowstation can be logged off at a time.
|
|
*/
|
|
if (!(dwFlags & EWX_SHUTDOWN) &&
|
|
pwinsta != gpwinstaLogoff) {
|
|
return STATUS_DEVICE_BUSY;
|
|
}
|
|
|
|
/*
|
|
* Set the new flags
|
|
*/
|
|
gdwShutdownFlags = dwFlags;
|
|
|
|
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 (Thread->Cid.UniqueProcess != gpidLogon) {
|
|
if (dwFlags & EWX_NOTIFY) {
|
|
if (ptiClient && ptiClient->TIF_flags & TIF_16BIT)
|
|
gptiShutdownNotify = ptiClient;
|
|
dwFlags &= ~EWX_NOTIFY;
|
|
*lpdwFlags = dwFlags;
|
|
}
|
|
|
|
if (NotifyLogon(pwinsta, &luidCaller, dwFlags))
|
|
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;
|
|
|
|
dwThreadEndSession = (DWORD)PsGetCurrentThread()->Cid.UniqueThread;
|
|
gpwinstaLogoff = pwinsta;
|
|
pwinsta->luidEndSession = luidCaller;
|
|
|
|
/*
|
|
* Lock the windowstation to prevent apps from starting
|
|
* while we're doing shutdown processing.
|
|
*/
|
|
gdwLocks = pwinsta->dwFlags & (WSF_SWITCHLOCK | WSF_OPENLOCK);
|
|
pwinsta->dwFlags |= (WSF_OPENLOCK | WSF_SHUTDOWN);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS EndShutdown(
|
|
PETHREAD Thread,
|
|
NTSTATUS StatusShutdown)
|
|
{
|
|
PWINDOWSTATION pwinsta = gpwinstaLogoff;
|
|
PDESKTOP pdesk;
|
|
LUID luidCaller;
|
|
|
|
UserAssert(gpwinstaLogoff);
|
|
|
|
gpwinstaLogoff = NULL;
|
|
dwThreadEndSession = 0;
|
|
pwinsta->dwFlags &= ~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->dwFlags =
|
|
(pwinsta->dwFlags & ~WSF_OPENLOCK) |
|
|
gdwLocks;
|
|
|
|
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)) {
|
|
|
|
/*
|
|
* Save the current user's NumLock state
|
|
*/
|
|
if (FastOpenProfileUserMapping()) {
|
|
RegisterPerUserKeyboardIndicators();
|
|
FastCloseProfileUserMapping();
|
|
}
|
|
|
|
/*
|
|
* Zero out the free blocks in all desktop heaps.
|
|
*/
|
|
for (pdesk = pwinsta->rpdeskList; pdesk != NULL; pdesk = pdesk->rpdeskNext) {
|
|
RtlZeroHeap(pdesk->hheapDesktop, 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 (bFontsAreLoaded) {
|
|
LeaveCrit();
|
|
GreRemoveAllButPermanentFonts();
|
|
EnterCrit();
|
|
bFontsAreLoaded = FALSE;
|
|
}
|
|
} else {
|
|
pwinsta->dwFlags &= ~WSF_OPENLOCK;
|
|
}
|
|
|
|
/*
|
|
* Tell winlogon that we successfully shutdown/logged off.
|
|
*/
|
|
NotifyLogon(pwinsta, &luidCaller, gdwShutdownFlags);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxClientShutdown2
|
|
*
|
|
* Called by xxxClientShutdown
|
|
\***************************************************************************/
|
|
|
|
BOOL xxxClientShutdown2(
|
|
PBWL pbwl,
|
|
UINT msg,
|
|
DWORD wParam)
|
|
{
|
|
HWND *phwnd;
|
|
PWND pwnd;
|
|
TL tlpwnd;
|
|
BOOL fEnd;
|
|
PTHREADINFO ptiCurrent;
|
|
BOOL fDestroyTimers;
|
|
LPARAM lParam;
|
|
|
|
|
|
ptiCurrent = PtiCurrent();
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
break;
|
|
|
|
case WM_ENDSESSION:
|
|
xxxSendMessage(pwnd, WM_ENDSESSION, wParam, lParam);
|
|
fEnd = TRUE;
|
|
|
|
if (fDestroyTimers) {
|
|
DestroyWindowsTimers(pwnd);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
ThreadUnlock(&tlpwnd);
|
|
|
|
if (!fEnd)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
/***************************************************************************\
|
|
* xxxClientShutdown
|
|
*
|
|
* This is the processing that occurs when an application receives a
|
|
* WM_CLIENTSHUTDOWN message.
|
|
*
|
|
* 10-01-92 ScottLu Created.
|
|
\***************************************************************************/
|
|
|
|
void xxxClientShutdown(
|
|
PWND pwnd,
|
|
DWORD wParam,
|
|
DWORD lParam)
|
|
{
|
|
PBWL pbwl;
|
|
PTHREADINFO ptiT;
|
|
BOOL fExit;
|
|
|
|
/*
|
|
* Build a list of windows first.
|
|
*/
|
|
fExit = TRUE;
|
|
ptiT = GETPTI(pwnd);
|
|
|
|
/*
|
|
* If the request was cancelled, then do nothing.
|
|
*/
|
|
if (ptiT->TIF_flags & TIF_SHUTDOWNCOMPLETE) {
|
|
return;
|
|
}
|
|
|
|
if ((pbwl = BuildHwndList(ptiT->rpdesk->pDeskInfo->spwnd->spwndChild,
|
|
BWL_ENUMLIST, ptiT)) == NULL) {
|
|
/*
|
|
* Can't allocate memory to notify this thread's windows of shutdown.
|
|
*/
|
|
goto SafeExit;
|
|
}
|
|
|
|
if (wParam & WMCS_QUERYEND) {
|
|
fExit = xxxClientShutdown2(pbwl, WM_QUERYENDSESSION, wParam);
|
|
} else {
|
|
xxxClientShutdown2(pbwl, WM_ENDSESSION, wParam);
|
|
fExit = TRUE;
|
|
}
|
|
|
|
FreeHwndList(pbwl);
|
|
|
|
SafeExit:
|
|
ptiT->TIF_flags |= (TIF_SHUTDOWNCOMPLETE | (fExit ? TIF_ALLOWSHUTDOWN : 0));
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* 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;
|
|
|
|
//
|
|
// Allocate the per wow process info stuff
|
|
// ensuring the memory is Zero init.
|
|
//
|
|
pwpi = (PWOWPROCESSINFO) UserAllocPoolWithQuota(sizeof(WOWPROCESSINFO), TAG_WOW);
|
|
if (!pwpi)
|
|
return FALSE;
|
|
RtlZeroMemory(pwpi, sizeof(*pwpi));
|
|
|
|
//
|
|
// Reference the WowExec event for kernel access
|
|
//
|
|
bRetVal = NT_SUCCESS(ObReferenceObjectByHandle(
|
|
hEventWowExec,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
UserMode,
|
|
&pwpi->pEventWowExec,
|
|
NULL
|
|
));
|
|
|
|
//
|
|
// if sucess then intialize the pwpi, ppi structs
|
|
// else free allocated memory
|
|
//
|
|
if (bRetVal) {
|
|
pwpi->hEventWowExecClient = hEventWowExec;
|
|
pwpi->lpfnWowExitTask = (DWORD)pfnW32EndTask;
|
|
ppi = PpiCurrent();
|
|
ppi->pwpi = pwpi;
|
|
|
|
// add to the list, order doesn't matter
|
|
pwpi->pwpiNext = gpwpiFirstWow;
|
|
gpwpiFirstWow = pwpi;
|
|
|
|
}
|
|
else {
|
|
UserFreePool(pwpi);
|
|
}
|
|
|
|
return bRetVal;
|
|
}
|