2025-04-27 07:49:33 -04:00

1608 lines
47 KiB
C

/**************************** 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;
}
/*
* Be sure that we are not in CSRSS context.
* WARNING: If for any reason we changed this to run in CSRSS context then we have to use
* LpcRequestPort instead of LpcRequestWaitReplyPort.
*/
UserAssert (!ISCSRSS());
LeaveCrit();
/*
* Create the desktop thread.
*/
Status = LpcRequestWaitReplyPort(CsrApiPort, (PPORT_MESSAGE)&m, (PPORT_MESSAGE)&m);
if (!NT_SUCCESS(Status)) {
EnterCrit();
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;
}
/***************************************************************************\
* xxxCreateWindowStation
*
* Creates the specified windowstation and starts a logon thread for the
* station.
*
* History:
* 01-15-91 JimA Created.
\***************************************************************************/
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;
}
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];
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 (pwinsta->pGlobalAtomTable == NULL) {
UserAssert(!NT_SUCCESS(Status));
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(pwinsta)) {
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;
UserAssert(pTerm->ptiDesktop->ppi->W32PF_Flags & W32PF_CLASSESREGISTERED);
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();
zzzSetDesktop(ptiCurrent, NULL, NULL);
/*
* 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(
(DWORD)0,
(PLARGE_STRING)DESKTOPCLASS,
NULL,
(WS_POPUP | WS_CLIPCHILDREN),
SHRT_MIN / 2,
SHRT_MIN / 2,
SHRT_MAX,
SHRT_MAX,
NULL,
NULL,
hModuleWin,
(LPWSTR)NULL,
VER31
);
if (pwnd == NULL) {
RIPMSG0(RIP_WARNING, "xxxCreateWindowStation: 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);
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);
/*
* Restore caller's ppi
*/
ptiCurrent->ppi = ppiSave;
/*
* Restore the previous desktop
*/
zzzSetDesktop(ptiCurrent, pdeskTemp, hdeskTemp);
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,
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, 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 DBG
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 = %#lx",
Status);
}
#endif
} 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);
return NULL;
}
/***************************************************************************\
* 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) {
PKL pkl = pwinsta->spklList;
PKL pklFirst = pkl;
RIPMSG2(RIP_WARNING, "FreeWindowStation: pwinsta(%p)->spklList is not NULL, %p", pwinsta, pwinsta->spklList);
do {
PKL pklNext = pkl->pklNext;
HMMarkObjectDestroy(pkl);
pkl->dwKL_Flags |= KL_UNLOADED;
Lock(&pwinsta->spklList, pklNext);
pkl = pklNext;
} while (pkl != pkl->pklNext && pkl != pklFirst);
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.
* station.
*
* 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)
;
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.
\***************************************************************************/
NTSTATUS WindowStationOpenProcedure(
PKWIN32_OPENMETHOD_PARAMETERS pOpenParams)
{
UNREFERENCED_PARAMETER(pOpenParams);
#if 0
/*
* Stop if someone is openning a windowstation handle cross session.
*/
if ((pOpenParams->Process != NULL)
&& (PsGetProcessSessionId(pOpenParams->Process) != gSessionId)
&& KD_DEBUGGER_ENABLED) {
DbgBreakPoint();
}
#endif
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;
}
/*
* 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 %#p while still in use", pwinsta);
return STATUS_UNSUCCESSFUL;
}
return STATUS_SUCCESS;
}
/***************************************************************************\
* _OpenWindowStation
*
* Open a windowstation for the calling process
*
* History:
* 03-19-91 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 is
* appropriate.
*/
Status = ObOpenObjectByName(
pObjA,
*ExWindowStationObjectType,
AccessMode,
NULL,
dwDesiredAccess,
NULL,
&hwinsta);
if (!NT_SUCCESS(Status)) {
RIPNTERR0(Status, RIP_VERBOSE, "");
hwinsta = NULL;
}
return hwinsta;
}
/***************************************************************************\
* _CloseWindowStation
*
* 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;
}
/***************************************************************************\
* xxxSetProcessWindowStation (API)
*
* Sets the windowstation of the calling process to the windowstation
* specified by pwinsta.
*
* History:
* 01-14-91 JimA Created.
\***************************************************************************/
NTSTATUS
xxxSetProcessWindowStation(
HWINSTA hwinsta,
KPROCESSOR_MODE AccessMode)
{
PETHREAD Thread = PsGetCurrentThread();
PEPROCESS Process = PsGetCurrentProcess();
HWINSTA hwinstaDup, hwinstaProcess;
NTSTATUS Status;
PPROCESSINFO ppi;
PWINDOWSTATION pwinsta;
PWINDOWSTATION pwinstaOld;
OBJECT_HANDLE_INFORMATION ohi;
OBJECT_HANDLE_INFORMATION ohiOld;
if (Process == NULL) {
UserAssert(Process);
return STATUS_UNSUCCESSFUL;
}
if (Thread == NULL) {
UserAssert(Thread);
return STATUS_UNSUCCESSFUL;
}
ppi = PpiFromProcess(PsGetThreadProcess(Thread));
Status = ObReferenceObjectByHandle(
hwinsta,
0,
*ExWindowStationObjectType,
AccessMode,
&pwinsta,
&ohi);
if (!NT_SUCCESS(Status)) {
RIPNTERR1(Status, RIP_WARNING, "Failed to reference windowstation with Status = 0x%#lx",
Status);
return Status;
}
/*
* Bug 38780. Lock the handle to window station so that an app cannot free the
* this handle by calling GetProcessWindowStation() & CloseHandle()
*/
/*
* Unprotect the old hwinsta
*/
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,
&ohiOld);
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);
#if DBG
RIPMSG2(RIP_WARNING,
"SetProcessWindowStation: Couldn't reference old WindowStation (0x%X) Status=0x%X",
hwinstaProcess,
Status);
#endif
}
}
/*
* Duplicate the WindowStation handle and stash it in the atom
* manager's cache (Process->Win32WindowStation). We duplicate
* the handle in case
*/
hwinstaProcess = PsGetProcessWin32WindowStation(Process);
if (hwinstaProcess == NULL) {
Status = xxxUserDuplicateObject(
NtCurrentProcess(),
hwinsta,
NtCurrentProcess(),
&hwinstaDup,
0,
0,
DUPLICATE_SAME_ACCESS);
if (NT_SUCCESS(Status)) {
PsSetProcessWindowStation(Process, hwinstaDup);
}
#if DBG
else {
RIPMSG2(RIP_WARNING,
"SetProcessWindowStation: Couldn't duplicate WindowStation handle (0x%X) Status=0x%X",
hwinsta,
Status);
}
#endif
}
ppi->amwinsta = ohi.GrantedAccess;
/*
* Cache WSF_NOIO flag in the W32PROCESS so that GDI can access it.
*/
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;
ppi = PpiCurrent();
UserAssert(ppi);
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;
/*
* Note -- NameList is client-side, and so must be protected.
*/
try {
ccxpNameList->cNames = 0;
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
return STATUS_ACCESS_VIOLATION;
}
ccxpwchDest = ccxpNameList->awchNames;
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, KernelMode, 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);
ccxpwchDest = ccxpNameList->awchNames;
Status = STATUS_BUFFER_TOO_SMALL;
}
try {
ccxpNameList->cNames++;
/*
* Copy and terminate the string
*/
RtlCopyMemory(ccxpwchDest, pNameInfo->Name.Buffer,
pNameInfo->Name.Length);
(PBYTE)ccxpwchDest += pNameInfo->Name.Length;
*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 {
*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/Atom table to use rather than the process.
* This allows NetDDE, which has threads running under
* different desktops on different windowstations but whos
* 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 (GetCurrentProcessId() != gpidLogon) {
RIPERR0(ERROR_ACCESS_DENIED,
RIP_WARNING,
"Access denied in _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;
}
#ifdef LATER // HY
BOOL _IsIoDesktop(
HDESK hdesk)
{
BOOL fRet = FALSE;
NTSTATUS Status;
PDESKTOP pdesk = NULL;
Status = ValidateHdesk(hdesk, UserMode, 0, &pdesk);
if (NT_SUCCESS(Status)) {
UserAssert(pdesk && pdesk->rpwinstaParent);
fRet = (pdesk->rpwinstaParent->dwWSF_Flags & WSF_NOIO) == 0;
ObDereferenceObject(pdesk);
}
return fRet;
}
#endif