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.
 
 
 
 
 
 

1631 lines
51 KiB

/**************************** Module Header ********************************\
* Module Name: winsta.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* Windowstation Routines
*
* History:
* 01-14-91 JimA Created.
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
/***************************************************************************\
* InitTerminal
*
* Creates the desktop thread for a terminal and also the RIT for the
* IO terminal
*
* History:
* 27-10-97 CLupu Created.
\***************************************************************************/
NTSTATUS xxxInitTerminal(
PTERMINAL pTerm)
{
NTSTATUS Status;
PKEVENT pEventTermInit;
HANDLE hEventInputReady, hEventTermInit;
USER_API_MSG m;
CheckCritIn();
UserAssert(!(pTerm->dwTERMF_Flags & TERMF_INITIALIZED));
if (pTerm->pEventInputReady != NULL) {
/*
* if we make it here it means that another thread is
* executing xxxInitTerminal for the same terminal and it
* left the critical section.
*/
UserAssert(pTerm->pEventTermInit != NULL);
/*
* use a local variable so we can safely reset
* pTerm->pEventTermInit when we're done with it
*/
pEventTermInit = pTerm->pEventTermInit;
ObReferenceObject(pEventTermInit);
LeaveCrit();
goto Wait;
}
/*
* Create the input ready event. RIT and desktop thread will wait for it.
* It will be set when the first desktop in this terminal will be created.
*/
Status = ZwCreateEvent(
&hEventInputReady,
EVENT_ALL_ACCESS,
NULL,
NotificationEvent,
FALSE);
if (!NT_SUCCESS(Status))
return Status;
Status = ObReferenceObjectByHandle(
hEventInputReady,
EVENT_ALL_ACCESS,
*ExEventObjectType,
KernelMode,
&pTerm->pEventInputReady, NULL);
ZwClose(hEventInputReady);
if (!NT_SUCCESS(Status))
return Status;
/*
* Device and RIT initialization. Don't do it for
* the system terminal.
*/
if (!(pTerm->dwTERMF_Flags & TERMF_NOIO)) {
if (!CreateTerminalInput(pTerm)) {
ObDereferenceObject(pTerm->pEventInputReady);
return STATUS_NO_MEMORY;
}
}
/*
* create an event to syncronize the terminal initialization
*/
Status = ZwCreateEvent(
&hEventTermInit,
EVENT_ALL_ACCESS,
NULL,
NotificationEvent,
FALSE);
if (!NT_SUCCESS(Status)) {
ObDereferenceObject(pTerm->pEventInputReady);
return Status;
}
Status = ObReferenceObjectByHandle(
hEventTermInit,
EVENT_ALL_ACCESS,
*ExEventObjectType,
KernelMode,
&pTerm->pEventTermInit, NULL);
ZwClose(hEventTermInit);
if (!NT_SUCCESS(Status)) {
ObDereferenceObject(pTerm->pEventInputReady);
return Status;
}
/*
* use a local variable so we can safely reset
* pTerm->pEventTermInit when we're done with it
*/
pEventTermInit = pTerm->pEventTermInit;
if (!InitCreateSystemThreadsMsg(&m, CST_DESKTOP, pTerm, 0, FALSE)) {
ObDereferenceObject(pTerm->pEventInputReady);
ObDereferenceObject(pEventTermInit);
return STATUS_NO_MEMORY;
}
LeaveCrit();
/*
* Create the desktop thread.
*/
if (ISCSRSS()) {
/*
* Windows bug: 452899
* Since we are in CSRSS context use LpcRequestPort to send LPC_DATAGRAM message type,
* Do not use LpcRequestWaitReplyPort because it will send LPC_REQUEST which will
* fail (in server side).
*/
RIPMSGF1(RIP_WARNING, "Desktop Thread for term=%p is being created within CSRSS context.", pTerm);
Status = LpcRequestPort(CsrApiPort, (PPORT_MESSAGE)&m);
} else {
Status = LpcRequestWaitReplyPort(CsrApiPort, (PPORT_MESSAGE)&m, (PPORT_MESSAGE)&m);
}
if (!NT_SUCCESS(Status) || (!ISCSRSS() && !NT_SUCCESS(m.ReturnValue))) {
EnterCrit();
RIPMSGF1(RIP_WARNING, "Failed to create a desktop thread with 0x%x ... bailing out.", m.ReturnValue);
ObDereferenceObject(pTerm->pEventInputReady);
ObDereferenceObject(pEventTermInit);
return STATUS_NO_MEMORY;
}
Wait:
KeWaitForSingleObject(pEventTermInit,
WrUserRequest,
KernelMode,
FALSE,
NULL);
EnterCrit();
/*
* dereference the terminal init event. It will eventually
* go away.
*/
ObDereferenceObject(pEventTermInit);
pTerm->pEventTermInit = NULL;
if (pTerm->dwTERMF_Flags & TERMF_DTINITFAILED) {
return STATUS_NO_MEMORY;
}
pTerm->dwTERMF_Flags |= TERMF_INITIALIZED;
return STATUS_SUCCESS;
}
static CONST LPCWSTR lpszStdFormats[] = {
L"StdExit",
L"StdNewDocument",
L"StdOpenDocument",
L"StdEditDocument",
L"StdNewfromTemplate",
L"StdCloseDocument",
L"StdShowItem",
L"StdDoVerbItem",
L"System",
L"OLEsystem",
L"StdDocumentName",
L"Protocols",
L"Topics",
L"Formats",
L"Status",
L"EditEnvItems",
L"True",
L"False",
L"Change",
L"Save",
L"Close",
L"MSDraw"
};
NTSTATUS CreateGlobalAtomTable(
PVOID* ppAtomTable)
{
NTSTATUS Status;
RTL_ATOM Atom;
ULONG i;
Status = RtlCreateAtomTable(0, ppAtomTable);
if (!NT_SUCCESS(Status)) {
RIPMSG0(RIP_WARNING, "Global atom table not created");
return Status;
}
for (i = 0; i < ARRAY_SIZE(lpszStdFormats); i++) {
Status = RtlAddAtomToAtomTable(*ppAtomTable,
(PWSTR)lpszStdFormats[i],
&Atom);
if (!NT_SUCCESS(Status)) {
RIPMSG1(RIP_WARNING,
"RtlAddAtomToAtomTable failed to add atom %ws",
lpszStdFormats[i]);
RtlDestroyAtomTable(*ppAtomTable);
return Status;
}
RtlPinAtomInAtomTable(*ppAtomTable, Atom);
}
return Status;
}
/***************************************************************************\
* xxxCreateWindowStation
*
* Creates the specified windowstation and starts a logon thread for the
* station.
*
* History:
* 01-15-91 JimA Created.
\***************************************************************************/
HWINSTA xxxCreateWindowStation(
POBJECT_ATTRIBUTES ObjectAttributes,
KPROCESSOR_MODE OwnershipMode,
DWORD dwDesiredAccess,
HANDLE hKbdLayoutFile,
DWORD offTable,
PKBDTABLE_MULTI_INTERNAL pKbdTableMulti,
PCWSTR pwszKLID,
UINT uKbdInputLocale)
{
PWINDOWSTATION pwinsta;
PTHREADINFO ptiCurrent;
PDESKTOP pdeskTemp;
HDESK hdeskTemp;
PSECURITY_DESCRIPTOR psd;
PSECURITY_DESCRIPTOR psdCapture;
PPROCESSINFO ppiSave;
NTSTATUS Status;
PACCESS_ALLOWED_ACE paceList = NULL, pace;
ULONG ulLength, ulLengthSid;
HANDLE hEvent;
HWINSTA hwinsta;
DWORD dwDisableHooks;
PTERMINAL pTerm = NULL;
PWND pwnd;
WCHAR szBaseNamedObjectDirectory[MAX_SESSION_PATH];
BOOL bMDWCreated = FALSE;
UserAssert(IsWinEventNotifyDeferredOK());
/*
* Get the pointer to the security descriptor so we can
* assign it to the new object later.
*/
psdCapture = ObjectAttributes->SecurityDescriptor;
/*
* The first windowstation that gets created is Winsta0 and
* it's the only interactive one.
*/
if (grpWinStaList == NULL) {
/*
* Assert that winlogon is the first to call CreateWindowStation
*/
UserAssert(PsGetCurrentProcessId() == gpidLogon);
pTerm = &gTermIO;
} else {
pTerm = &gTermNOIO;
UserAssert(grpWinStaList->rpwinstaNext == NULL ||
pTerm->dwTERMF_Flags & TERMF_NOIO);
pTerm->dwTERMF_Flags |= TERMF_NOIO;
}
/*
* Create the WindowStation object
*/
Status = ObCreateObject(KernelMode, *ExWindowStationObjectType,
ObjectAttributes, OwnershipMode, NULL, sizeof(WINDOWSTATION),
0, 0, &pwinsta);
if (!NT_SUCCESS(Status)) {
RIPNTERR0(Status, RIP_WARNING, "Failed to create windowstation");
return NULL;
}
/*
* WindowStation object was created then reference gWinstaRunRef.
* We have to dereference it at FreeWindowStation().
* And wait for any live objects to get freed in Win32KDriverUnload().
*/
if (!ExAcquireRundownProtection(&gWinstaRunRef)) {
goto create_error;
}
/*
* Initialize everything.
*/
RtlZeroMemory(pwinsta, sizeof(WINDOWSTATION));
/*
* Store the session id of the session who created the windowstation
*/
pwinsta->dwSessionId = gSessionId;
pwinsta->pTerm = pTerm;
/*
* All the windowstations in the system terminal are non-interactive.
*/
if (pTerm->dwTERMF_Flags & TERMF_NOIO) {
pwinsta->dwWSF_Flags = WSF_NOIO;
}
/*
* Create the global atom table and populate it with the default OLE atoms
* Pin each atom so they can't be deleted by bogus applications like Winword
*/
Status = CreateGlobalAtomTable(&pwinsta->pGlobalAtomTable);
if (!NT_SUCCESS(Status)) {
UserAssert(pwinsta->pGlobalAtomTable == NULL);
RIPNTERR0(Status, RIP_WARNING, "CreateGlobalAtomTable failed");
goto create_error;
}
/*
* create the desktop thread
* and the RIT (only for the IO terminal)
*/
if (!(pTerm->dwTERMF_Flags & TERMF_INITIALIZED)) {
Status = xxxInitTerminal(pTerm);
if (!NT_SUCCESS(Status)) {
RIPNTERR0(Status, RIP_WARNING, "xxxInitTerminal failed");
goto create_error;
}
}
if (!(pwinsta->dwWSF_Flags & WSF_NOIO)) {
if (!xxxInitWindowStation()) {
RIPNTERR0(STATUS_NO_MEMORY, RIP_WARNING, "xxxInitWindowStation failed");
goto create_error;
}
}
/*
* Create only one desktop owner window per terminal.
*/
if (pTerm->spwndDesktopOwner == NULL) {
/*
* Switch ppi values so window will be created using the
* system's desktop window class.
*/
ptiCurrent = PtiCurrent();
ppiSave = ptiCurrent->ppi;
ptiCurrent->ppi = pTerm->ptiDesktop->ppi;
#ifndef LAZY_CLASS_INIT
UserAssert(pTerm->ptiDesktop->ppi->W32PF_Flags & W32PF_CLASSESREGISTERED);
#endif
pdeskTemp = ptiCurrent->rpdesk; /* save current desktop */
hdeskTemp = ptiCurrent->hdesk;
if (pdeskTemp) {
ObReferenceObject(pdeskTemp);
LogDesktop(pdeskTemp, LD_REF_FN_CREATEWINDOWSTATION, TRUE, (ULONG_PTR)PtiCurrent());
}
/*
* The following code is not supposed to leave the critical section because
* CreateWindowStation is an API so the current thread can be on any state
* setting its pdesk to NULL it's kind of bogus
*/
DeferWinEventNotify();
BEGINATOMICCHECK();
if (zzzSetDesktop(ptiCurrent, NULL, NULL) == FALSE) {
Status = STATUS_NO_MEMORY;
EXITATOMICCHECK();
zzzEndDeferWinEventNotify();
/*
* Restore caller's ppi
*/
ptiCurrent->ppi = ppiSave;
goto create_error;
}
/*
* HACK HACK HACK!!! (adams) In order to create the desktop window
* with the correct desktop, we set the desktop of the current thread
* to the new desktop. But in so doing we allow hooks on the current
* thread to also hook this new desktop. This is bad, because we don't
* want the desktop window to be hooked while it is created. So we
* temporarily disable hooks of the current thread and desktop, and
* reenable them after switching back to the original desktop.
*/
dwDisableHooks = ptiCurrent->TIF_flags & TIF_DISABLEHOOKS;
ptiCurrent->TIF_flags |= TIF_DISABLEHOOKS;
/*
* Create the desktop owner window
*
* CONSIDER (adams): Do we want to limit the desktop size so that the
* width and height of a rect will fit in 16bit coordinates?
*
* SHRT_MIN / 2, SHRT_MIN / 2, SHRT_MAX, SHRT_MAX,
*
* Or do we want to limit it so just any point has 16bit coordinates?
*
* -SHRT_MIN, -SHRT_MIN, SHRT_MAX * 2, SHRT_MAX * 2
*/
pwnd = xxxNVCreateWindowEx(0,
(PLARGE_STRING)DESKTOPCLASS,
NULL,
WS_POPUP | WS_CLIPCHILDREN,
SHRT_MIN / 2,
SHRT_MIN / 2,
SHRT_MAX,
SHRT_MAX,
NULL,
NULL,
hModuleWin,
NULL,
VER31);
if (pwnd == NULL) {
RIPMSGF0(RIP_WARNING, "Failed to create mother desktop window");
Status = STATUS_NO_MEMORY;
EXITATOMICCHECK();
zzzEndDeferWinEventNotify();
/*
* Restore caller's ppi
*/
ptiCurrent->ppi = ppiSave;
/*
* Restore the previous desktop
*/
zzzSetDesktop(ptiCurrent, pdeskTemp, hdeskTemp);
goto create_error;
}
/*
* Mark this handle entry that is allocated out of pool
*/
{
PHE phe;
UserAssert(ptiCurrent->rpdesk == NULL);
phe = HMPheFromObject(pwnd);
phe->bFlags |= HANDLEF_POOL;
}
Lock(&(pTerm->spwndDesktopOwner), pwnd);
pTerm->dwTERMF_Flags |= TERMF_MOTHERWND_CREATED;
UserAssert(ptiCurrent->TIF_flags & TIF_DISABLEHOOKS);
ptiCurrent->TIF_flags = (ptiCurrent->TIF_flags & ~TIF_DISABLEHOOKS) | dwDisableHooks;
SetVisible(pTerm->spwndDesktopOwner, SV_SET);
HMChangeOwnerThread(pTerm->spwndDesktopOwner, pTerm->ptiDesktop);
bMDWCreated = TRUE;
/*
* Restore caller's ppi
*/
ptiCurrent->ppi = ppiSave;
/*
* Restore the previous desktop
*/
if (zzzSetDesktop(ptiCurrent, pdeskTemp, hdeskTemp) == FALSE) {
Status = STATUS_NO_MEMORY;
EXITATOMICCHECK();
zzzEndDeferWinEventNotify();
goto create_error;
}
ENDATOMICCHECK();
zzzEndDeferWinEventNotify();
if (pdeskTemp) {
LogDesktop(pdeskTemp, LD_DEREF_FN_CREATEWINDOWSTATION, FALSE, (ULONG_PTR)PtiCurrent());
ObDereferenceObject(pdeskTemp);
}
}
/*
* If this is the visible windowstation, assign it to
* the server and create the desktop switch notification
* event.
*/
if (!(pwinsta->dwWSF_Flags & WSF_NOIO)) {
UNICODE_STRING strName;
HANDLE hRootDir;
OBJECT_ATTRIBUTES obja;
/*
* Create desktop switch notification event.
*/
ulLengthSid = RtlLengthSid(SeExports->SeWorldSid);
ulLength = ulLengthSid + sizeof(ACE_HEADER) + sizeof(ACCESS_MASK);
/*
* Allocate the ACE list
*/
paceList = (PACCESS_ALLOWED_ACE)UserAllocPoolWithQuota(ulLength, TAG_SECURITY);
if (paceList == NULL) {
Status = STATUS_NO_MEMORY;
goto create_error;
}
/*
* Initialize ACE 0
*/
pace = paceList;
pace->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
pace->Header.AceSize = (USHORT)ulLength;
pace->Header.AceFlags = 0;
pace->Mask = SYNCHRONIZE;
RtlCopySid(ulLengthSid, &pace->SidStart, SeExports->SeWorldSid);
/*
* Create the SD
*/
psd = CreateSecurityDescriptor(paceList, ulLength, FALSE);
UserFreePool(paceList);
if (psd == NULL) {
Status = STATUS_NO_MEMORY;
goto create_error;
}
/*
* Create the named event.
*/
UserAssert(ghEventSwitchDesktop == NULL);
if (gbRemoteSession) {
swprintf(szBaseNamedObjectDirectory, L"\\Sessions\\%ld\\BaseNamedObjects",
gSessionId);
RtlInitUnicodeString(&strName, szBaseNamedObjectDirectory);
} else {
RtlInitUnicodeString(&strName, L"\\BaseNamedObjects");
}
InitializeObjectAttributes(&obja,
&strName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
Status = ZwOpenDirectoryObject(&hRootDir,
DIRECTORY_ALL_ACCESS &
~(DELETE | WRITE_DAC | WRITE_OWNER),
&obja);
if (NT_SUCCESS(Status)) {
RtlInitUnicodeString(&strName, L"WinSta0_DesktopSwitch");
InitializeObjectAttributes(&obja,
&strName,
OBJ_OPENIF | OBJ_KERNEL_HANDLE,
hRootDir,
psd);
Status = ZwCreateEvent(&hEvent, EVENT_ALL_ACCESS, &obja,
NotificationEvent, FALSE);
ZwClose(hRootDir);
if (NT_SUCCESS(Status)) {
Status = ObReferenceObjectByHandle(hEvent, EVENT_ALL_ACCESS, *ExEventObjectType,
KernelMode, &gpEventSwitchDesktop, NULL);
if (NT_SUCCESS(Status)) {
/*
* Attach to the system process and create a handle to the
* object. This will ensure that the object name is retained
* when hEvent is closed. This is simpler than creating a
* permanent object, which takes the
* SeCreatePermanentPrivilege.
*/
KeAttachProcess(PsGetProcessPcb(gpepCSRSS));
Status = ObOpenObjectByPointer(
gpEventSwitchDesktop,
0,
NULL,
EVENT_ALL_ACCESS,
NULL,
KernelMode,
&ghEventSwitchDesktop);
KeDetachProcess();
}
ZwClose(hEvent);
}
}
if (!NT_SUCCESS(Status)) {
goto create_error;
}
UserFreePool(psd);
}
/*
* Create a handle to the windowstation.
*/
Status = ObInsertObject(pwinsta, NULL, dwDesiredAccess, 1,
&pwinsta, &hwinsta);
if (Status == STATUS_OBJECT_NAME_EXISTS) {
/*
* The windowstation already exists, so deref and leave.
*/
ObDereferenceObject(pwinsta);
} else if (NT_SUCCESS(Status)) {
PSECURITY_DESCRIPTOR psdParent = NULL, psdNew;
SECURITY_SUBJECT_CONTEXT Context;
POBJECT_DIRECTORY pParentDirectory;
SECURITY_INFORMATION siNew;
BOOLEAN MemoryAllocated = FALSE;
/*
* Create security descriptor for the windowstation.
* ObInsertObject only supports non-container
* objects, so we must assign our own security descriptor.
*/
SeCaptureSubjectContext(&Context);
SeLockSubjectContext(&Context);
pParentDirectory = OBJECT_HEADER_TO_NAME_INFO(
OBJECT_TO_OBJECT_HEADER(pwinsta))->Directory;
if (pParentDirectory != NULL) {
Status = ObGetObjectSecurity(
pParentDirectory,
&psdParent,
&MemoryAllocated);
if ( !NT_SUCCESS(Status) ) {
goto create_error;
}
}
Status = SeAssignSecurity(
psdParent,
psdCapture,
&psdNew,
TRUE,
&Context,
(PGENERIC_MAPPING)&WinStaMapping,
PagedPool);
ObReleaseObjectSecurity(psdParent, MemoryAllocated);
SeUnlockSubjectContext(&Context);
SeReleaseSubjectContext(&Context);
if (!NT_SUCCESS(Status)) {
if (Status == STATUS_ACCESS_DENIED) {
RIPNTERR0(Status,
RIP_WARNING,
"Access denied during object creation");
} else {
RIPNTERR1(Status,
RIP_ERROR,
"Can't create security descriptor! Status = 0x%x",
Status);
}
} else {
/*
* Call the security method to copy the security descriptor
*/
siNew = (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION);
Status = ObSetSecurityDescriptorInfo(
pwinsta,
&siNew,
psdNew,
&OBJECT_TO_OBJECT_HEADER(pwinsta)->SecurityDescriptor,
PagedPool,
(PGENERIC_MAPPING)&WinStaMapping);
SeDeassignSecurity(&psdNew);
if (NT_SUCCESS(Status)) {
PWINDOWSTATION *ppwinsta;
/*
* Put it on the tail of the global windowstation list
*/
ppwinsta = &grpWinStaList;
while (*ppwinsta != NULL)
ppwinsta = &(*ppwinsta)->rpwinstaNext;
LockWinSta(ppwinsta, pwinsta);
/*
* For interactive window stations load the keyboard
* layout.
*/
if ((pwinsta->dwWSF_Flags & WSF_NOIO) == 0 && pwszKLID != NULL) {
TL tlpwinsta;
PushW32ThreadLock(pwinsta, &tlpwinsta, UserDereferenceObject);
if (xxxLoadKeyboardLayoutEx(
pwinsta,
hKbdLayoutFile,
(HKL)NULL,
offTable,
pKbdTableMulti,
pwszKLID,
uKbdInputLocale,
KLF_ACTIVATE | KLF_INITTIME) == NULL) {
Status = STATUS_UNSUCCESSFUL;
}
PopW32ThreadLock(&tlpwinsta);
}
}
}
ObDereferenceObject(pwinsta);
}
if (!NT_SUCCESS(Status)) {
RIPNTERR1(Status,
RIP_WARNING,
"CreateWindowStation: Failed with Status 0x%x",
Status);
return NULL;
}
return hwinsta;
/*
* Goto here if an error occurs so things can be cleaned up.
*/
create_error:
RIPNTERR1(Status,
RIP_WARNING,
"CreateWindowStation: Failed with Status 0x%x",
Status);
ObDereferenceObject(pwinsta);
if (bMDWCreated) {
/*
* Switch ppi values so that HMChangeOwnerThread() can find system's
* desktop window class.
*/
ppiSave = ptiCurrent->ppi;
ptiCurrent->ppi = pTerm->ptiDesktop->ppi;
HMChangeOwnerThread(pTerm->spwndDesktopOwner, ptiCurrent);
xxxCleanupMotherDesktopWindow(pTerm);
/*
* Restore caller's ppi
*/
ptiCurrent->ppi = ppiSave;
}
return NULL;
}
__inline VOID MarkKLUnloaded(
PKL pkl)
{
HMMarkObjectDestroy(pkl);
pkl->dwKL_Flags |= KL_UNLOADED;
}
/***************************************************************************\
* FreeWindowStation
*
* Called when last lock to the windowstation is removed. Frees all resources
* owned by the windowstation.
*
* History:
* 12-22-93 JimA Created.
\***************************************************************************/
NTSTATUS FreeWindowStation(
PKWIN32_DELETEMETHOD_PARAMETERS pDeleteParams)
{
PWINDOWSTATION pwinsta = pDeleteParams->Object;
UserAssert(OBJECT_TO_OBJECT_HEADER(pwinsta)->Type == *ExWindowStationObjectType);
/*
* Mark the windowstation as dying. Make sure we're not recursing.
*/
UserAssert(!(pwinsta->dwWSF_Flags & WSF_DYING));
pwinsta->dwWSF_Flags |= WSF_DYING;
UserAssert(pwinsta->rpdeskList == NULL);
/*
* Free up the other resources.
*/
if (!(pwinsta->dwWSF_Flags & WSF_NOIO) && gpEventSwitchDesktop != NULL) {
KeSetEvent(gpEventSwitchDesktop, EVENT_INCREMENT, FALSE);
ObDereferenceObject(gpEventSwitchDesktop);
gpEventSwitchDesktop = NULL;
}
BEGIN_REENTERCRIT();
RtlDestroyAtomTable(pwinsta->pGlobalAtomTable);
ForceEmptyClipboard(pwinsta);
/*
* Free up keyboard layouts.
*/
if (!(pwinsta->dwWSF_Flags & WSF_NOIO) && pwinsta->spklList != NULL) {
#if DBG
int iter = 0;
#endif
PKL pklLast = pwinsta->spklList->pklPrev;
RIPMSG2(RIP_WARNING, "FreeWindowStation: pwinsta(%p)->spklList is not NULL, %p", pwinsta, pwinsta->spklList);
while (pwinsta->spklList != pklLast) {
PKL pklNext = pwinsta->spklList->pklNext;
#if DBG
++iter;
UserAssert(iter < 1000);
#endif
MarkKLUnloaded(pwinsta->spklList);
Lock(&pwinsta->spklList, pklNext);
}
MarkKLUnloaded(pwinsta->spklList);
Unlock(&pwinsta->spklList);
HYDRA_HINT(HH_KBDLYOUTFREEWINSTA);
/*
* make sure the logon notify window went away
*/
UserAssert(gspwndLogonNotify == NULL);
} else {
UserAssert(pwinsta->spklList == NULL);
}
/*
* Free the USER sid.
*/
if (pwinsta->psidUser != NULL) {
UserFreePool(pwinsta->psidUser);
pwinsta->psidUser = NULL;
}
/*
* Dereference gWinstaRunRef, because it was referenced at WindowStation
* creation time in xxxCreateWindowStation().
*/
ExReleaseRundownProtection(&gWinstaRunRef);
END_REENTERCRIT();
return STATUS_SUCCESS;
}
/***************************************************************************\
* DestroyWindowStation
*
* Removes the windowstation from the global list. We can't release any
* resources until all locks have been removed.
*
* History:
* 01-17-91 JimA Created.
\***************************************************************************/
NTSTATUS DestroyWindowStation(
PKWIN32_CLOSEMETHOD_PARAMETERS pCloseParams)
{
PWINDOWSTATION pwinsta = pCloseParams->Object;
PWINDOWSTATION *ppwinsta;
PDESKTOP pdesk;
PDESKTOP pdeskLock = NULL;
UserAssert(OBJECT_TO_OBJECT_HEADER(pCloseParams->Object)->Type == *ExWindowStationObjectType);
/*
* If this is not the last handle, leave.
*/
if (pCloseParams->SystemHandleCount != 1) {
return STATUS_SUCCESS;
}
BEGIN_REENTERCRIT();
/*
* If the window station was linked into the terminal's list, go ahead
* and unlink it.
*/
for (ppwinsta = &grpWinStaList;
*ppwinsta != NULL && pwinsta != *ppwinsta;
ppwinsta = &(*ppwinsta)->rpwinstaNext) {
/* do nothing */;
}
if (*ppwinsta != NULL) {
UnlockWinSta(ppwinsta);
/*
* Assert that unlocking it didn't destroy it.
*/
UserAssert(OBJECT_TO_OBJECT_HEADER(pCloseParams->Object)->Type == *ExWindowStationObjectType);
*ppwinsta = pwinsta->rpwinstaNext;
/*
* The instruction above transfered rpwinstaNext lock ownership to
* the previous element in the list. Hence the value in pwinsta can
* no longer be considered valid.
*/
pwinsta->rpwinstaNext = NULL;
}
/*
* Notify all console threads and wait for them to terminate.
*/
pdesk = pwinsta->rpdeskList;
while (pdesk != NULL) {
if (pdesk != grpdeskLogon && pdesk->dwConsoleThreadId) {
LockDesktop(&pdeskLock, pdesk, LDL_FN_DESTROYWINDOWSTATION, 0);
TerminateConsole(pdesk);
/*
* Restart scan in case desktop list has changed.
*/
pdesk = pwinsta->rpdeskList;
UnlockDesktop(&pdeskLock, LDU_FN_DESTROYWINDOWSTATION, 0);
} else {
pdesk = pdesk->rpdeskNext;
}
}
END_REENTERCRIT();
return STATUS_SUCCESS;
}
/***************************************************************************\
* WindowStationOpenProcedure
*
* History:
* 06-11-01 GerardoB Created for instrumentation/debugging purposes.
* 02-26-02 Msadek Change it to deny any open with granted special
* rights cross session.
\***************************************************************************/
NTSTATUS WindowStationOpenProcedure(
PKWIN32_OPENMETHOD_PARAMETERS pOpenParams)
{
/*
* Allow windowstation open cross session only if no special rights
* granted.
*/
if (pOpenParams->GrantedAccess & SPECIFIC_RIGHTS_ALL) {
if (PsGetProcessSessionId(pOpenParams->Process) !=
((PWINDOWSTATION)pOpenParams->Object)->dwSessionId) {
return STATUS_ACCESS_DENIED;
}
}
return STATUS_SUCCESS;
}
/***************************************************************************\
* ParseWindowStation
*
* Parse a windowstation path.
*
* History:
* 06-14-95 JimA Created.
\***************************************************************************/
NTSTATUS ParseWindowStation(
PKWIN32_PARSEMETHOD_PARAMETERS pParseParams)
{
PWINDOWSTATION pwinsta = pParseParams->ParseObject;
UserAssert(OBJECT_TO_OBJECT_HEADER(pParseParams->ParseObject)->Type == *ExWindowStationObjectType);
/*
* If nothing remains to be parsed, return the windowstation.
*/
*(pParseParams->Object) = NULL;
if (pParseParams->RemainingName->Length == 0) {
if (pParseParams->ObjectType != *ExWindowStationObjectType) {
return STATUS_OBJECT_TYPE_MISMATCH;
}
ObReferenceObject(pwinsta);
*(pParseParams->Object) = pwinsta;
return STATUS_SUCCESS;
}
/*
* Skip leading path separator, if present.
*/
if (*(pParseParams->RemainingName->Buffer) == OBJ_NAME_PATH_SEPARATOR) {
pParseParams->RemainingName->Buffer++;
pParseParams->RemainingName->Length -= sizeof(WCHAR);
pParseParams->RemainingName->MaximumLength -= sizeof(WCHAR);
}
/*
* Validate the desktop name.
*/
if (wcschr(pParseParams->RemainingName->Buffer, L'\\')) {
return STATUS_OBJECT_PATH_INVALID;
}
if (pParseParams->ObjectType == *ExDesktopObjectType) {
return ParseDesktop(pParseParams->ParseObject,
pParseParams->ObjectType,
pParseParams->AccessState,
pParseParams->AccessMode,
pParseParams->Attributes,
pParseParams->CompleteName,
pParseParams->RemainingName,
pParseParams->Context,
pParseParams->SecurityQos,
pParseParams->Object);
}
return STATUS_OBJECT_TYPE_MISMATCH;
}
/***************************************************************************\
* OkayToCloseWindowStation
*
* We can only close windowstation handles if they're not in use.
*
* History:
* 08-Feb-1999 JerrySh Created.
\***************************************************************************/
NTSTATUS OkayToCloseWindowStation(
PKWIN32_OKAYTOCLOSEMETHOD_PARAMETERS pOkCloseParams)
{
PWINDOWSTATION pwinsta = (PWINDOWSTATION)pOkCloseParams->Object;
UserAssert(OBJECT_TO_OBJECT_HEADER(pOkCloseParams->Object)->Type == *ExWindowStationObjectType);
/*
* Kernel mode code can close anything.
*/
if (pOkCloseParams->PreviousMode == KernelMode) {
return STATUS_SUCCESS;
/*
* Do not allow a user mode process to close a kernel handle of ours.
* It shouldn't. In addition, if this happens cross-session, we will try to
* attach to the system processes and will bugcheck since the seesion
* address space is not mapped into it. Same for the session manager
* process. See bug# 759533.
*/
} else if (PsGetProcessSessionIdEx(pOkCloseParams->Process) == -1) {
return STATUS_ACCESS_DENIED;
}
/*
* We can't close a windowstation that's being used.
*/
if (CheckHandleInUse(pOkCloseParams->Handle) ||
CheckHandleFlag(pOkCloseParams->Process, pwinsta->dwSessionId, pOkCloseParams->Handle, HF_PROTECTED)) {
RIPMSG1(RIP_WARNING,
"Trying to close windowstation 0x%p while still in use",
pwinsta);
return STATUS_UNSUCCESSFUL;
}
return STATUS_SUCCESS;
}
/***************************************************************************\
* _OpenWindowStation
*
* Opens the specified windowstation.
*
* History:
* 03-19-1991 JimA Created.
\***************************************************************************/
HWINSTA _OpenWindowStation(
POBJECT_ATTRIBUTES pObjA,
DWORD dwDesiredAccess,
KPROCESSOR_MODE AccessMode)
{
HWINSTA hwinsta;
NTSTATUS Status;
/*
* Obja is client-side. Ob interfaces protect and capture as
* appropriate.
*/
Status = ObOpenObjectByName(pObjA,
*ExWindowStationObjectType,
AccessMode,
NULL,
dwDesiredAccess,
NULL,
&hwinsta);
if (!NT_SUCCESS(Status)) {
try {
RIPNTERR3(Status,
RIP_VERBOSE,
"Opening of winsta %.*ws failed with Status 0x%x",
pObjA->ObjectName->Length,
pObjA->ObjectName->Buffer,
Status);
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
}
hwinsta = NULL;
}
return hwinsta;
}
/***************************************************************************\
* _CloseWindowStation (API)
*
* Closes a windowstation for the calling process
*
* History:
* 15-Jun-1999 JerrySh Created.
\***************************************************************************/
BOOL _CloseWindowStation(
HWINSTA hwinsta)
{
HWINSTA hwinstaCurrent;
_GetProcessWindowStation(&hwinstaCurrent);
if (hwinsta != hwinstaCurrent) {
return NT_SUCCESS(ZwClose(hwinsta));
}
return FALSE;
}
/***************************************************************************\
* _SetProcessWindowStation (API)
*
* Sets the windowstation of the calling process to the windowstation
* specified by pwinsta.
*
* History:
* 01-14-91 JimA Created.
\***************************************************************************/
NTSTATUS _SetProcessWindowStation(
HWINSTA hwinsta,
KPROCESSOR_MODE AccessMode)
{
PETHREAD Thread = PsGetCurrentThread();
PEPROCESS Process = PsGetCurrentProcess();
HWINSTA hwinstaDup, hwinstaProcess;
NTSTATUS Status;
PPROCESSINFO ppi;
PWINDOWSTATION pwinsta, pwinstaOld;
OBJECT_HANDLE_INFORMATION ohi;
Status = ObReferenceObjectByHandle(hwinsta,
0,
*ExWindowStationObjectType,
AccessMode,
&pwinsta,
&ohi);
if (!NT_SUCCESS(Status)) {
RIPNTERR1(Status,
RIP_WARNING,
"Failed to reference windowstation with Status = 0x%x",
Status);
return Status;
}
if (pwinsta->dwSessionId != gSessionId) {
RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "Cannot assign a window station of a different session");
ObDereferenceObject(pwinsta);
return STATUS_INVALID_PARAMETER;
}
/*
* We need to lock the winsta handle so that an app cannot free it by
* calling GetProcessWindowStation() & CloseHandle().
*/
/*
* Unprotect the old hwinsta.
*/
ppi = PpiFromProcess(PsGetThreadProcess(Thread));
if (ppi->hwinsta) {
SetHandleFlag(ppi->hwinsta, HF_PROTECTED, FALSE);
}
/*
* Save the WindowStation information.
*/
LockWinSta(&ppi->rpwinsta, pwinsta);
ObDereferenceObject(pwinsta);
ppi->hwinsta = hwinsta;
/*
* Protect the new Window Station Handle
*/
SetHandleFlag(ppi->hwinsta, HF_PROTECTED, TRUE);
/*
* Check the old Atom Manager WindowStation to see if we are
* changing this process' WindowStation.
*/
hwinstaProcess = PsGetProcessWin32WindowStation(Process);
if (hwinstaProcess != NULL) {
/*
* Get a pointer to the old WindowStation object to see if it's
* the same WindowStation that we are setting.
*/
Status = ObReferenceObjectByHandle(hwinstaProcess,
0,
*ExWindowStationObjectType,
AccessMode,
&pwinstaOld,
NULL);
if (NT_SUCCESS(Status)) {
/*
* Are they different WindowStations? If so, NULL out the
* atom manager cache so we will reset it below.
*/
if (pwinsta != pwinstaOld) {
ZwClose(hwinstaProcess);
PsSetProcessWindowStation(Process, NULL);
}
ObDereferenceObject(pwinstaOld);
} else {
/*
* Their Atom Manager handle is bad? Give them a new one.
*/
PsSetProcessWindowStation(Process, NULL);
RIPMSGF2(RIP_WARNING,
"Couldn't reference old WindowStation (0x%x) Status=0x%x",
hwinstaProcess,
Status);
}
}
/*
* Duplicate the WindowStation handle and stash it in the atom manager's
* cache (Process->Win32WindowStation).
*/
hwinstaProcess = PsGetProcessWin32WindowStation(Process);
if (hwinstaProcess == NULL) {
Status = ZwDuplicateObject(NtCurrentProcess(),
hwinsta,
NtCurrentProcess(),
&hwinstaDup,
0,
0,
DUPLICATE_SAME_ACCESS);
if (NT_SUCCESS(Status)) {
PsSetProcessWindowStation(Process, hwinstaDup);
} else {
RIPMSGF2(RIP_WARNING,
"Couldn't dup WindowStation handle (0x%x) Status 0x%x",
hwinsta,
Status);
}
}
ppi->amwinsta = ohi.GrantedAccess;
if (pwinsta->dwWSF_Flags & WSF_NOIO) {
ppi->W32PF_Flags &= ~W32PF_IOWINSTA;
} else {
ppi->W32PF_Flags |= W32PF_IOWINSTA;
}
/*
* Do the access check now for readscreen so that blts off of the
* display will be as fast as possible.
*/
if (RtlAreAllAccessesGranted(ohi.GrantedAccess, WINSTA_READSCREEN)) {
ppi->W32PF_Flags |= W32PF_READSCREENACCESSGRANTED;
} else {
ppi->W32PF_Flags &= ~W32PF_READSCREENACCESSGRANTED;
}
return STATUS_SUCCESS;
}
/***************************************************************************\
* _GetProcessWindowStation (API)
*
* Returns a pointer to the windowstation of the calling process.
*
* History:
* 01-14-91 JimA Created.
\***************************************************************************/
PWINDOWSTATION _GetProcessWindowStation(
HWINSTA *phwinsta)
{
PPROCESSINFO ppi = PpiCurrent();
if (phwinsta) {
*phwinsta = ppi->hwinsta;
}
return ppi->rpwinsta;
}
/***************************************************************************\
* _BuildNameList
*
* Builds a list of windowstation or desktop names.
*
* History:
* 05-17-94 JimA Created.
* 10-21-96 CLupu Added TERMINAL enumeration
\***************************************************************************/
NTSTATUS _BuildNameList(
PWINDOWSTATION pwinsta,
PNAMELIST ccxpNameList,
UINT cbNameList,
PUINT pcbNeeded)
{
PBYTE pobj;
PWCHAR ccxpwchDest, ccxpwchMax;
ACCESS_MASK amDesired;
POBJECT_HEADER pHead;
POBJECT_HEADER_NAME_INFO pNameInfo;
DWORD iNext;
NTSTATUS Status;
CONST GENERIC_MAPPING *pGenericMapping;
try {
ccxpNameList->cNames = 0;
ccxpwchDest = ccxpNameList->awchNames;
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
return STATUS_ACCESS_VIOLATION;
}
ccxpwchMax = (PWCHAR)((PBYTE)ccxpNameList + cbNameList - sizeof(WCHAR));
/*
* If we're enumerating windowstations, pwinsta is NULL. Otherwise,
* we're enumerating desktops.
*/
if (pwinsta == NULL) {
pobj = (PBYTE)grpWinStaList;
amDesired = WINSTA_ENUMERATE;
pGenericMapping = &WinStaMapping;
iNext = FIELD_OFFSET(WINDOWSTATION, rpwinstaNext);
} else {
pobj = (PBYTE)pwinsta->rpdeskList;
amDesired = DESKTOP_ENUMERATE;
pGenericMapping = &DesktopMapping;
iNext = FIELD_OFFSET(DESKTOP, rpdeskNext);
}
Status = STATUS_SUCCESS;
*pcbNeeded = 0;
while (pobj != NULL) {
if (AccessCheckObject(pobj, amDesired, UserMode, pGenericMapping)) {
/*
* Find object name.
*/
pHead = OBJECT_TO_OBJECT_HEADER(pobj);
pNameInfo = OBJECT_HEADER_TO_NAME_INFO(pHead);
if (pNameInfo == NULL) {
goto NEXT_ITERATION;
}
/*
* If we run out of space, reset the buffer and continue so we
* can compute the needed space.
*/
if ((PWCHAR)((PBYTE)ccxpwchDest + pNameInfo->Name.Length +
sizeof(WCHAR)) >= ccxpwchMax) {
*pcbNeeded += (UINT)((PBYTE)ccxpwchDest - (PBYTE)ccxpNameList);
try {
ccxpwchDest = ccxpNameList->awchNames;
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
return STATUS_ACCESS_VIOLATION;
}
Status = STATUS_BUFFER_TOO_SMALL;
}
try {
/*
* If the entire buffer is sufficient for the new string,
* the copy operation is performed, otherwise the size of
* this new string is noted and the copy is skipped.
*/
ccxpNameList->cNames++;
if ((PWCHAR)((PBYTE)ccxpwchDest + pNameInfo->Name.Length +
sizeof(WCHAR)) <= ccxpwchMax) {
/*
* Copy and terminate the string
*/
RtlCopyMemory(ccxpwchDest, pNameInfo->Name.Buffer, pNameInfo->Name.Length);
(PBYTE)ccxpwchDest += pNameInfo->Name.Length;
UserAssert(ccxpwchDest <= (ccxpwchMax - sizeof(WCHAR)));
*ccxpwchDest++ = 0;
}
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
return STATUS_ACCESS_VIOLATION;
}
}
NEXT_ITERATION:
pobj = *(PBYTE*)(pobj + iNext);
}
/*
* Put an empty string on the end.
*/
try {
UserAssert(ccxpwchDest <= (PWCHAR)((PBYTE)ccxpNameList + cbNameList - sizeof(WCHAR)));
*ccxpwchDest++ = 0;
ccxpNameList->cb = (UINT)((PBYTE)ccxpwchDest - (PBYTE)ccxpNameList);
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
return STATUS_ACCESS_VIOLATION;
}
*pcbNeeded += (UINT)((PBYTE)ccxpwchDest - (PBYTE)ccxpNameList);
return Status;
}
NTSTATUS ReferenceWindowStation(
PETHREAD Thread,
HWINSTA hwinsta,
ACCESS_MASK amDesiredAccess,
PWINDOWSTATION *ppwinsta,
BOOL fUseDesktop)
{
PPROCESSINFO ppi;
PTHREADINFO pti;
PWINDOWSTATION pwinsta = NULL;
NTSTATUS Status;
/*
* We prefer to use the thread's desktop to dictate which windowstation to
* use rather than the process. This allows NetDDE, which has threads
* running under different desktops on different windowstations but whose
* process is set to only one of these windowstations, to get global atoms
* properly without having to change its process windowstation a billion
* times and synchronize.
*/
ppi = PpiFromProcess(PsGetThreadProcess(Thread));
pti = PtiFromThread(Thread);
/*
* First, try to get the windowstation from the pti, and then from the
* ppi.
*/
if (ppi != NULL) {
if (!fUseDesktop || pti == NULL || pti->rpdesk == NULL ||
ppi->rpwinsta == pti->rpdesk->rpwinstaParent) {
/*
* Use the windowstation assigned to the process.
*/
pwinsta = ppi->rpwinsta;
if (pwinsta != NULL) {
RETURN_IF_ACCESS_DENIED(ppi->amwinsta, amDesiredAccess,
STATUS_ACCESS_DENIED);
}
}
/*
* If we aren't using the process' windowstation, try to go through
* the thread's desktop.
*/
if (pwinsta == NULL && pti != NULL && pti->rpdesk != NULL) {
/*
* Perform access check the parent windowstation. This
* is an expensive operation.
*/
pwinsta = pti->rpdesk->rpwinstaParent;
if (!AccessCheckObject(pwinsta, amDesiredAccess, KernelMode, &WinStaMapping))
return STATUS_ACCESS_DENIED;
}
}
/*
* If we still don't have a windowstation and a handle was passed in, use
* it.
*/
if (pwinsta == NULL) {
if (hwinsta != NULL) {
Status = ObReferenceObjectByHandle(hwinsta,
amDesiredAccess,
*ExWindowStationObjectType,
KernelMode,
&pwinsta,
NULL);
if (!NT_SUCCESS(Status)) {
return Status;
}
ObDereferenceObject(pwinsta);
} else {
return STATUS_NOT_FOUND;
}
}
*ppwinsta = pwinsta;
return STATUS_SUCCESS;
}
/***************************************************************************\
* _SetWindowStationUser
*
* Private API for winlogon to associate a windowstation with a user.
*
* History:
* 06-27-94 JimA Created.
\***************************************************************************/
BOOL _SetWindowStationUser(
PWINDOWSTATION pwinsta,
PLUID pluidUser,
PSID ccxpsidUser,
DWORD cbsidUser)
{
/*
* Make sure the caller is the logon process.
*/
if (PsGetCurrentProcessId() != gpidLogon) {
RIPERR0(ERROR_ACCESS_DENIED,
RIP_WARNING,
"_SetWindowStationUser: Caller must be in the logon process");
return FALSE;
}
if (pwinsta->psidUser != NULL) {
UserFreePool(pwinsta->psidUser);
}
if (ccxpsidUser != NULL) {
pwinsta->psidUser = UserAllocPoolWithQuota(cbsidUser, TAG_SECURITY);
if (pwinsta->psidUser == NULL) {
RIPERR0(ERROR_OUTOFMEMORY,
RIP_WARNING,
"Memory allocation failed in _SetWindowStationUser");
return FALSE;
}
try {
RtlCopyMemory(pwinsta->psidUser, ccxpsidUser, cbsidUser);
} except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
UserFreePool(pwinsta->psidUser);
pwinsta->psidUser = NULL;
return FALSE;
}
} else {
pwinsta->psidUser = NULL;
}
pwinsta->luidUser = *pluidUser;
return TRUE;
}
/***************************************************************************\
* _LockWorkStation (API)
*
* locks the workstation. This API just posts a message to winlogon
* and winlogon does all the work
*
* History:
* 06-11-97 CLupu Created.
\***************************************************************************/
BOOL _LockWorkStation(
VOID)
{
UserAssert(gspwndLogonNotify != NULL);
_PostMessage(gspwndLogonNotify,
WM_LOGONNOTIFY, LOGON_LOCKWORKSTATION, LOCK_NORMAL);
return TRUE;
}