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