1608 lines
47 KiB
C
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
|
|
|