Leaked source code of windows server 2003
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.
 
 
 
 
 
 

651 lines
19 KiB

/**************************** 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;
}