mirror of https://github.com/lianthony/NT4.0
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.
1190 lines
34 KiB
1190 lines
34 KiB
/**************************** Module Header ********************************\
|
|
* Module Name: winsta.c
|
|
*
|
|
* Copyright 1985-91, Microsoft Corporation
|
|
*
|
|
* Windowstation Routines
|
|
*
|
|
* History:
|
|
* 01-14-91 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#define N_ELEM(a) (sizeof(a)/sizeof(a[0]))
|
|
#define LAST_ELEM(a) ( (a) [ N_ELEM(a) - 1 ] )
|
|
#define PLAST_ELEM(a) (&LAST_ELEM(a))
|
|
|
|
/***************************************************************************\
|
|
* xxxCreateWindowStation
|
|
*
|
|
* Creates the specified windowstation and starts a logon thread for the
|
|
* station.
|
|
*
|
|
* History:
|
|
* 01-15-91 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
static 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",
|
|
NULL
|
|
};
|
|
|
|
HWINSTA xxxCreateWindowStation(
|
|
POBJECT_ATTRIBUTES ObjectAttributes,
|
|
KPROCESSOR_MODE ProbeMode,
|
|
DWORD dwDesiredAccess)
|
|
{
|
|
RTL_ATOM Atom;
|
|
PWINDOWSTATION pwinsta, *ppwinsta;
|
|
PTHREADINFO ptiCurrent;
|
|
DESKTOPTHREADINIT dti;
|
|
HANDLE hThreadDesktop;
|
|
PDESKTOP pdeskTemp;
|
|
HDESK hdeskTemp;
|
|
PSECURITY_DESCRIPTOR psd;
|
|
PSECURITY_DESCRIPTOR psdCapture;
|
|
PPROCESSINFO ppiSave;
|
|
NTSTATUS Status;
|
|
PACCESS_ALLOWED_ACE paceList = NULL, pace;
|
|
ULONG i, ulLength, ulLengthSid;
|
|
HANDLE hEvent;
|
|
HWINSTA hwinsta;
|
|
DWORD dwDisableHooks;
|
|
|
|
static BOOL fOneTimeInit = FALSE;
|
|
|
|
/*
|
|
* Get the pointer to the security descriptor so we can
|
|
* assign it to the new object later.
|
|
*/
|
|
try {
|
|
psdCapture = ObjectAttributes->SecurityDescriptor;
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
RIPNTERR0(GetExceptionCode(), RIP_VERBOSE, "");
|
|
return NULL;
|
|
}
|
|
|
|
Status = ObCreateObject(ProbeMode, *ExWindowStationObjectType,
|
|
ObjectAttributes, ProbeMode, NULL, sizeof(WINDOWSTATION),
|
|
0, 0, &pwinsta);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPNTERR0(Status, RIP_VERBOSE, "");
|
|
return NULL;
|
|
}
|
|
RtlZeroMemory(pwinsta, sizeof(WINDOWSTATION));
|
|
|
|
/*
|
|
* Initialize everything
|
|
*/
|
|
pwinsta->rpdeskList = NULL;
|
|
pwinsta->pwchDiacritic = PLAST_ELEM(pwinsta->awchDiacritic);
|
|
|
|
/*
|
|
* Only allow the first instance to do I/O
|
|
*/
|
|
if (fOneTimeInit)
|
|
pwinsta->dwFlags = 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 = RtlCreateAtomTable( 0, &pwinsta->pGlobalAtomTable );
|
|
if (!NT_SUCCESS(Status))
|
|
goto create_error;
|
|
for (i=0; lpszStdFormats[i] != NULL; i++) {
|
|
Status = RtlAddAtomToAtomTable( pwinsta->pGlobalAtomTable,
|
|
(PWSTR)lpszStdFormats[i],
|
|
&Atom
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
goto create_error;
|
|
|
|
RtlPinAtomInAtomTable( pwinsta->pGlobalAtomTable, Atom );
|
|
#if DBG
|
|
if (i==0) {
|
|
RIPMSG3(RIP_VERBOSE,
|
|
"Created atom table 0x%08lx for winsta 0x%08lx (First OLE atom is 0x%08lx)",
|
|
pwinsta->pGlobalAtomTable,
|
|
pwinsta,
|
|
Atom);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* NT-specific stuff
|
|
*/
|
|
pwinsta->rpdeskLogon = NULL;
|
|
Status = ZwCreateEvent(&hEvent, EVENT_ALL_ACCESS, NULL,
|
|
NotificationEvent, FALSE);
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = ObReferenceObjectByHandle(hEvent, EVENT_ALL_ACCESS, NULL,
|
|
KernelMode, &pwinsta->pEventInputReady, NULL);
|
|
ZwClose(hEvent);
|
|
}
|
|
if (!NT_SUCCESS(Status))
|
|
goto create_error;
|
|
|
|
/*
|
|
* Device and RIT initialization
|
|
*/
|
|
if (!fOneTimeInit && !xxxInitWinStaDevices(pwinsta))
|
|
goto create_error;
|
|
|
|
/*
|
|
* Create the desktop thread in a suspended state.
|
|
*/
|
|
dti.pwinsta = pwinsta;
|
|
dti.pEvent = CreateKernelEvent(SynchronizationEvent, FALSE);
|
|
if (dti.pEvent == NULL) {
|
|
goto create_error;
|
|
}
|
|
LeaveCrit();
|
|
Status = CreateSystemThread((PKSTART_ROUTINE)DesktopThread, &dti,
|
|
&hThreadDesktop);
|
|
if (!NT_SUCCESS(Status)) {
|
|
EnterCrit();
|
|
UserFreePool(dti.pEvent);
|
|
goto create_error;
|
|
}
|
|
ZwClose(hThreadDesktop);
|
|
KeWaitForSingleObject(dti.pEvent, WrUserRequest, KernelMode, FALSE, NULL);
|
|
EnterCrit();
|
|
UserFreePool(dti.pEvent);
|
|
|
|
/*
|
|
* Switch ppi values so window will be created using the
|
|
* system's desktop window class.
|
|
*/
|
|
UserAssert(pwinsta->ptiDesktop->ppi->W32PF_Flags & W32PF_CLASSESREGISTERED);
|
|
ptiCurrent = PtiCurrent();
|
|
ppiSave = ptiCurrent->ppi;
|
|
ptiCurrent->ppi = pwinsta->ptiDesktop->ppi;
|
|
|
|
/*
|
|
* Create the desktop owner window
|
|
*/
|
|
pdeskTemp = ptiCurrent->rpdesk; /* save current desktop */
|
|
hdeskTemp = ptiCurrent->hdesk;
|
|
if (pdeskTemp)
|
|
ObReferenceObject(pdeskTemp);
|
|
|
|
SetDesktop(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;
|
|
|
|
Lock(&(pwinsta->spwndDesktopOwner),
|
|
xxxCreateWindowEx((DWORD)0,
|
|
(PLARGE_STRING)MAKEINTRESOURCE(DESKTOPCLASS),
|
|
NULL, (WS_POPUP | WS_CLIPCHILDREN), 0, 0,
|
|
0x10000, 0x10000, NULL, NULL, hModuleWin, (LPWSTR)NULL, VER31));
|
|
|
|
UserAssert(ptiCurrent->TIF_flags & TIF_DISABLEHOOKS);
|
|
ptiCurrent->TIF_flags = (ptiCurrent->TIF_flags & ~TIF_DISABLEHOOKS) | dwDisableHooks;
|
|
|
|
if (pwinsta->spwndDesktopOwner != NULL) {
|
|
SetWF(pwinsta->spwndDesktopOwner, WFVISIBLE);
|
|
HMChangeOwnerThread(pwinsta->spwndDesktopOwner, pwinsta->ptiDesktop);
|
|
}
|
|
|
|
/*
|
|
* Restore caller's ppi
|
|
*/
|
|
ptiCurrent->ppi = ppiSave;
|
|
|
|
/*
|
|
* Restore the previous desktop
|
|
*/
|
|
SetDesktop(ptiCurrent, pdeskTemp, hdeskTemp);
|
|
if (pdeskTemp)
|
|
ObDereferenceObject(pdeskTemp);
|
|
|
|
if (pwinsta->spwndDesktopOwner == NULL) {
|
|
goto create_error;
|
|
}
|
|
|
|
fOneTimeInit = TRUE;
|
|
|
|
/*
|
|
* If this is the visible windowstation, assign it to
|
|
* the server and create the desktop switch notification
|
|
* event.
|
|
*/
|
|
if (!(pwinsta->dwFlags & 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_SYSTEM);
|
|
if (paceList == NULL) {
|
|
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, 1, ulLength, FALSE);
|
|
UserFreePool(paceList);
|
|
|
|
/*
|
|
* Create the named event.
|
|
*/
|
|
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, NULL,
|
|
KernelMode, &pwinsta->pEventSwitchNotify, 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(&gpepSystem->Pcb);
|
|
Status = ObOpenObjectByPointer(
|
|
pwinsta->pEventSwitchNotify,
|
|
0,
|
|
NULL,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
KernelMode,
|
|
&pwinsta->hEventSwitchNotify);
|
|
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, psdNew;
|
|
SECURITY_SUBJECT_CONTEXT Context;
|
|
POBJECT_DIRECTORY pParentDirectory;
|
|
SECURITY_INFORMATION siNew;
|
|
|
|
/*
|
|
* 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)
|
|
psdParent = OBJECT_TO_OBJECT_HEADER(pParentDirectory)->SecurityDescriptor;
|
|
else
|
|
psdParent = NULL;
|
|
|
|
Status = SeAssignSecurity(
|
|
psdParent,
|
|
psdCapture,
|
|
&psdNew,
|
|
TRUE,
|
|
&Context,
|
|
&WinStaMapping,
|
|
PagedPool);
|
|
|
|
SeUnlockSubjectContext(&Context);
|
|
SeReleaseSubjectContext(&Context);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
#ifdef DEBUG
|
|
if (Status == STATUS_ACCESS_DENIED) {
|
|
RIPMSG0(RIP_WARNING, "Access denied during object creation");
|
|
} else {
|
|
RIPMSG1(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,
|
|
&WinStaMapping);
|
|
SeDeassignSecurity(&psdNew);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
/*
|
|
* Put it on the tail of the global windowstation list
|
|
*/
|
|
ppwinsta = &grpwinstaList;
|
|
while (*ppwinsta != NULL)
|
|
ppwinsta = &(*ppwinsta)->rpwinstaNext;
|
|
LockWinSta(ppwinsta, pwinsta);
|
|
}
|
|
}
|
|
ObDereferenceObject(pwinsta);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPNTERR0(Status, RIP_VERBOSE, "");
|
|
hwinsta = NULL;
|
|
}
|
|
|
|
return hwinsta;
|
|
|
|
/*
|
|
* Goto here if an error occurs so things can be cleaned up
|
|
*/
|
|
create_error:
|
|
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.
|
|
\***************************************************************************/
|
|
|
|
VOID FreeWindowStation(
|
|
PWINDOWSTATION pwinsta)
|
|
{
|
|
PWINDOWSTATION pwinstaLock = NULL;
|
|
BOOL fAlreadyHadCrit;
|
|
|
|
/*
|
|
* Mark the windowstation as dying. Make sure we're not recursing.
|
|
*/
|
|
UserAssert(!(pwinsta->dwFlags & WSF_DYING));
|
|
pwinsta->dwFlags |= WSF_DYING;
|
|
|
|
UserAssert(pwinsta->rpdeskList == NULL);
|
|
|
|
/*
|
|
* Free up the other resources
|
|
*/
|
|
if (pwinsta->pEventInputReady != NULL) {
|
|
KeSetEvent(pwinsta->pEventInputReady, EVENT_INCREMENT, FALSE);
|
|
ObDereferenceObject(pwinsta->pEventInputReady);
|
|
pwinsta->pEventInputReady = NULL;
|
|
}
|
|
if (pwinsta->pEventSwitchNotify != NULL) {
|
|
KeSetEvent(pwinsta->pEventSwitchNotify, EVENT_INCREMENT, FALSE);
|
|
ObDereferenceObject(pwinsta->pEventSwitchNotify);
|
|
pwinsta->pEventSwitchNotify = NULL;
|
|
}
|
|
|
|
/*
|
|
* Make sure that we have the user lock.
|
|
*/
|
|
fAlreadyHadCrit = ExIsResourceAcquiredExclusiveLite(gpresUser);
|
|
if (fAlreadyHadCrit == FALSE) {
|
|
EnterCrit();
|
|
}
|
|
|
|
RtlDestroyAtomTable(pwinsta->pGlobalAtomTable);
|
|
|
|
ForceEmptyClipboard(pwinsta);
|
|
|
|
/*
|
|
* Free up keyboard layouts
|
|
*/
|
|
if (!(pwinsta->dwFlags & WSF_NOIO))
|
|
xxxFreeKeyboardLayouts(pwinsta);
|
|
|
|
/*
|
|
* Kill desktop thread.
|
|
*/
|
|
UserAssert(pwinsta->spwndLogonNotify == NULL);
|
|
if (pwinsta->ptiDesktop != NULL) {
|
|
|
|
_PostThreadMessage(pwinsta->ptiDesktop, WM_QUIT, 0, 0);
|
|
|
|
/*
|
|
* Unlock the desktop owner window and allow the
|
|
* desktop thread to destroy it.
|
|
*/
|
|
Unlock(&pwinsta->spwndDesktopOwner);
|
|
|
|
/*
|
|
* We used to leave the critical section and wait here
|
|
* for the desktop thread to go away.
|
|
* This call usually happens while processing DeleteThreadInfo,
|
|
* so waiting for the desktop thread to go away might hang the
|
|
* kernel if another system thread is trying to exit at the
|
|
* same time. This can happen while shutting down a service
|
|
* process that creates a window station.
|
|
*
|
|
* All desktops must be gone or we wouldn't be here. In other
|
|
* words, nobody should need this winstation anymore; it
|
|
* should be OK to go on without waiting.
|
|
*
|
|
* Let's make sure nobody grabs this pwinsta though.
|
|
*/
|
|
|
|
UserAssert(pwinsta == pwinsta->ptiDesktop->pwinsta);
|
|
pwinsta->ptiDesktop->pwinsta = NULL;
|
|
}
|
|
|
|
if (fAlreadyHadCrit == FALSE) {
|
|
LeaveCrit();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* 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.
|
|
\***************************************************************************/
|
|
|
|
VOID DestroyWindowStation(
|
|
PEPROCESS Process,
|
|
PVOID pobj,
|
|
ACCESS_MASK amGranted,
|
|
ULONG cProcessHandles,
|
|
ULONG cSystemHandles)
|
|
{
|
|
PWINDOWSTATION pwinsta = pobj;
|
|
PWINDOWSTATION *ppwinsta;
|
|
PDESKTOP pdesk;
|
|
PDESKTOP pdeskLock = NULL;
|
|
BOOL fReenter;
|
|
|
|
/*
|
|
* If this is not the last handle, leave
|
|
*/
|
|
if (cSystemHandles != 1)
|
|
return;
|
|
|
|
/*
|
|
* If we do not own the resource, get it now
|
|
*/
|
|
fReenter = !ExIsResourceAcquiredExclusiveLite(gpresUser);
|
|
if (fReenter)
|
|
EnterCrit();
|
|
|
|
/*
|
|
* Unlink the object
|
|
*/
|
|
for (ppwinsta = &grpwinstaList; pwinsta != *ppwinsta;
|
|
ppwinsta = &(*ppwinsta)->rpwinstaNext)
|
|
;
|
|
UnlockWinSta(ppwinsta);
|
|
*ppwinsta = pwinsta->rpwinstaNext;
|
|
|
|
/*
|
|
* Close the switch event
|
|
*/
|
|
if (pwinsta->hEventSwitchNotify) {
|
|
KeAttachProcess(&gpepSystem->Pcb);
|
|
ZwClose(pwinsta->hEventSwitchNotify);
|
|
KeDetachProcess();
|
|
}
|
|
|
|
/*
|
|
* Notify all console threads and wait for them to
|
|
* terminate.
|
|
*/
|
|
pdesk = pwinsta->rpdeskList;
|
|
while (pdesk != NULL) {
|
|
if (pdesk != pwinsta->rpdeskLogon && pdesk->dwConsoleThreadId) {
|
|
LockDesktop(&pdeskLock, pdesk);
|
|
TerminateConsole(pdesk);
|
|
|
|
/*
|
|
* Restart scan in case desktop list has changed
|
|
*/
|
|
pdesk = pwinsta->rpdeskList;
|
|
UnlockDesktop(&pdeskLock);
|
|
} else
|
|
pdesk = pdesk->rpdeskNext;
|
|
}
|
|
|
|
if (fReenter)
|
|
LeaveCrit();
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* ParseWindowStation
|
|
*
|
|
* Parse a windowstation path.
|
|
*
|
|
* History:
|
|
* 06-14-95 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
NTSTATUS ParseWindowStation(
|
|
PVOID pContainerObject,
|
|
POBJECT_TYPE pObjectType,
|
|
PACCESS_STATE pAccessState,
|
|
KPROCESSOR_MODE AccessMode,
|
|
ULONG Attributes,
|
|
PUNICODE_STRING pstrCompleteName,
|
|
PUNICODE_STRING pstrRemainingName,
|
|
PVOID Context OPTIONAL,
|
|
PSECURITY_QUALITY_OF_SERVICE pqos,
|
|
PVOID *pObject)
|
|
{
|
|
PWINDOWSTATION pwinsta = pContainerObject;
|
|
|
|
/*
|
|
* If nothing remains to be parsed, return the windowstation.
|
|
*/
|
|
*pObject = NULL;
|
|
if (pstrRemainingName->Length == 0) {
|
|
if (pObjectType != *ExWindowStationObjectType)
|
|
return STATUS_OBJECT_TYPE_MISMATCH;
|
|
|
|
ObReferenceObject(pwinsta);
|
|
*pObject = pwinsta;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Skip leading path separator, if present.
|
|
*/
|
|
if (*(pstrRemainingName->Buffer) == OBJ_NAME_PATH_SEPARATOR) {
|
|
pstrRemainingName->Buffer++;
|
|
pstrRemainingName->Length -= sizeof(WCHAR);
|
|
pstrRemainingName->MaximumLength -= sizeof(WCHAR);
|
|
}
|
|
|
|
/*
|
|
* Validate the desktop name.
|
|
*/
|
|
if (wcschr(pstrRemainingName->Buffer, L'\\'))
|
|
return STATUS_OBJECT_PATH_INVALID;
|
|
if (pObjectType == *ExDesktopObjectType) {
|
|
return ParseDesktop(
|
|
pContainerObject,
|
|
pObjectType,
|
|
pAccessState,
|
|
AccessMode,
|
|
Attributes,
|
|
pstrCompleteName,
|
|
pstrRemainingName,
|
|
Context,
|
|
pqos,
|
|
pObject);
|
|
}
|
|
|
|
return STATUS_OBJECT_TYPE_MISMATCH;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* _OpenWindowStation
|
|
*
|
|
* Open a windowstation for the calling process
|
|
*
|
|
* History:
|
|
* 03-19-91 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
HWINSTA _OpenWindowStation(
|
|
POBJECT_ATTRIBUTES ObjA,
|
|
DWORD dwDesiredAccess)
|
|
{
|
|
HWINSTA hwinsta;
|
|
NTSTATUS Status;
|
|
|
|
Status = ObOpenObjectByName(
|
|
ObjA,
|
|
*ExWindowStationObjectType,
|
|
KeGetPreviousMode(),
|
|
NULL,
|
|
dwDesiredAccess,
|
|
NULL,
|
|
&hwinsta);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPNTERR0(Status, RIP_VERBOSE, "");
|
|
hwinsta = NULL;
|
|
}
|
|
return hwinsta;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxSetProcessWindowStation (API)
|
|
*
|
|
* Sets the windowstation of the calling process to the windowstation
|
|
* specified by pwinsta.
|
|
*
|
|
* History:
|
|
* 01-14-91 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
BOOL xxxSetProcessWindowStation(
|
|
HWINSTA hwinsta)
|
|
{
|
|
PETHREAD Thread = PsGetCurrentThread();
|
|
PEPROCESS Process = PsGetCurrentProcess();
|
|
HWINSTA hwinstaDup;
|
|
NTSTATUS Status;
|
|
PPROCESSINFO ppi;
|
|
PWINDOWSTATION pwinsta;
|
|
PWINDOWSTATION pwinstaOld;
|
|
OBJECT_HANDLE_INFORMATION ohi;
|
|
OBJECT_HANDLE_INFORMATION ohiOld;
|
|
|
|
if (Process == NULL) {
|
|
UserAssert(Process);
|
|
return FALSE;
|
|
}
|
|
|
|
if (Thread == NULL) {
|
|
UserAssert(Thread);
|
|
return FALSE;
|
|
}
|
|
|
|
ppi = PpiFromProcess(THREAD_TO_PROCESS(Thread));
|
|
|
|
if (!NT_SUCCESS(ObReferenceObjectByHandle(
|
|
hwinsta,
|
|
0,
|
|
*ExWindowStationObjectType,
|
|
KeGetPreviousMode(),
|
|
&pwinsta,
|
|
&ohi))) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* 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) {
|
|
Status = ProtectHandle(ppi->hwinsta, FALSE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG2(RIP_WARNING, "ProtectHandle(hwinsta (%lx),FALSE) : Failed with Status = %lx\n",
|
|
ppi->hwinsta, Status);
|
|
}
|
|
}
|
|
/*
|
|
* Save the WindowStation information
|
|
*/
|
|
LockWinSta(&ppi->rpwinsta, pwinsta);
|
|
ObDereferenceObject(pwinsta);
|
|
ppi->hwinsta = hwinsta;
|
|
|
|
/*
|
|
* Protect the new Window Station Handle
|
|
*/
|
|
Status = ProtectHandle(ppi->hwinsta, TRUE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG2(RIP_WARNING, "ProtectHandle(hwinsta (%lx),TRUE) : Failed with Status = %lx\n",
|
|
ppi->hwinsta, Status);
|
|
}
|
|
/*
|
|
* Check the old Atom Manager WindowStation to see if we are
|
|
* changing this process' WindowStation.
|
|
*/
|
|
if (Process->Win32WindowStation) {
|
|
/*
|
|
* Get a pointer to the old WindowStation object to see if it's
|
|
* the same WindowStation that we are setting.
|
|
*/
|
|
Status = ObReferenceObjectByHandle(
|
|
Process->Win32WindowStation,
|
|
0,
|
|
*ExWindowStationObjectType,
|
|
KeGetPreviousMode(),
|
|
&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(Process->Win32WindowStation);
|
|
Process->Win32WindowStation = NULL;
|
|
}
|
|
ObDereferenceObject(pwinstaOld);
|
|
|
|
} else {
|
|
/*
|
|
* Their Atom Manager handle is bad? Give them a new one.
|
|
*/
|
|
Process->Win32WindowStation = NULL;
|
|
#ifdef DBG
|
|
RIPMSG2(RIP_WARNING,
|
|
"SetProcessWindowStation: Couldn't reference old WindowStation (0x%X) Status=0x%X",
|
|
Process->Win32WindowStation,
|
|
Status);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Duplicate the WindowStation handle and stash it in the atom
|
|
* manager's cache (Process->Win32WindowStation). We duplicate
|
|
* the handle in case
|
|
*/
|
|
if (Process->Win32WindowStation == NULL) {
|
|
Status = xxxUserDuplicateObject(
|
|
NtCurrentProcess(),
|
|
hwinsta,
|
|
NtCurrentProcess(),
|
|
&hwinstaDup,
|
|
0,
|
|
0,
|
|
DUPLICATE_SAME_ACCESS);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Process->Win32WindowStation = hwinstaDup;
|
|
}
|
|
#ifdef 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->dwFlags & 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 TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* _GetProcessWindowStation (API)
|
|
*
|
|
* Returns a pointer to the windowstation of the calling process.
|
|
*
|
|
* History:
|
|
* 01-14-91 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
PWINDOWSTATION _GetProcessWindowStation(
|
|
HWINSTA *phwinsta)
|
|
{
|
|
PETHREAD Thread = PsGetCurrentThread();
|
|
PPROCESSINFO ppi;
|
|
|
|
if (Thread == NULL) {
|
|
UserAssert(Thread);
|
|
return NULL;
|
|
}
|
|
|
|
ppi = PpiFromProcess(THREAD_TO_PROCESS(Thread));
|
|
if (phwinsta)
|
|
*phwinsta = ppi->hwinsta;
|
|
return ppi->rpwinsta;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* _BuildNameList
|
|
*
|
|
* Builds a list of windowstation or desktop names.
|
|
*
|
|
* History:
|
|
* 05-17-94 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
NTSTATUS _BuildNameList(
|
|
PWINDOWSTATION pwinsta,
|
|
PNAMELIST pNameList,
|
|
UINT cbNameList,
|
|
PUINT pcbNeeded)
|
|
{
|
|
PBYTE pobj;
|
|
PWCHAR pwchDest, pwchMax;
|
|
ACCESS_MASK amDesired;
|
|
POBJECT_HEADER pHead;
|
|
POBJECT_HEADER_NAME_INFO pNameInfo;
|
|
DWORD iNext;
|
|
NTSTATUS Status;
|
|
|
|
pNameList->cNames = 0;
|
|
pwchDest = pNameList->awchNames;
|
|
pwchMax = (PWCHAR)((PBYTE)pNameList + 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;
|
|
iNext = FIELD_OFFSET(WINDOWSTATION, rpwinstaNext);
|
|
} else {
|
|
pobj = (PBYTE)pwinsta->rpdeskList;
|
|
amDesired = DESKTOP_ENUMERATE;
|
|
iNext = FIELD_OFFSET(DESKTOP, rpdeskNext);
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
*pcbNeeded = 0;
|
|
while (pobj != NULL) {
|
|
|
|
if (AccessCheckObject(pobj, amDesired)) {
|
|
|
|
/*
|
|
* Find object name
|
|
*/
|
|
pHead = OBJECT_TO_OBJECT_HEADER(pobj);
|
|
pNameInfo = OBJECT_HEADER_TO_NAME_INFO(pHead);
|
|
|
|
/*
|
|
* If we run out of space, reset the buffer
|
|
* and continue so we can compute the needed
|
|
* space.
|
|
*/
|
|
if ((PWCHAR)((PBYTE)pwchDest + pNameInfo->Name.Length +
|
|
sizeof(WCHAR)) >= pwchMax) {
|
|
*pcbNeeded += (PBYTE)pwchDest - (PBYTE)pNameList;
|
|
pwchDest = pNameList->awchNames;
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
pNameList->cNames++;
|
|
|
|
/*
|
|
* Copy and terminate the string
|
|
*/
|
|
RtlCopyMemory(pwchDest, pNameInfo->Name.Buffer,
|
|
pNameInfo->Name.Length);
|
|
(PBYTE)pwchDest += pNameInfo->Name.Length;
|
|
*pwchDest++ = 0;
|
|
}
|
|
|
|
pobj = *(PBYTE *)(pobj + iNext);
|
|
}
|
|
|
|
/*
|
|
* Put an empty string on the end.
|
|
*/
|
|
*pwchDest++ = 0;
|
|
|
|
pNameList->cb = (PBYTE)pwchDest - (PBYTE)pNameList;
|
|
*pcbNeeded += (PBYTE)pwchDest - (PBYTE)pNameList;
|
|
|
|
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(Thread->ThreadsProcess);
|
|
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))
|
|
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,
|
|
KeGetPreviousMode(),
|
|
&pwinsta,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
ObDereferenceObject(pwinsta);
|
|
} else {
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
*ppwinsta = pwinsta;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* _UserGetGlobalAtomTable
|
|
*
|
|
* Private API for kernel atom manager to get a pointer to the windowstation's atom
|
|
* table.
|
|
*
|
|
* History:
|
|
* 04-20-94 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
NTSTATUS _UserGetGlobalAtomTable(
|
|
PETHREAD Thread,
|
|
HWINSTA hwinsta,
|
|
PVOID *ppGlobalAtomTable)
|
|
{
|
|
PWINDOWSTATION pwinsta;
|
|
PVOID GlobalAtomTable;
|
|
NTSTATUS Status;
|
|
|
|
GlobalAtomTable = NULL;
|
|
if (hwinsta == NULL) {
|
|
hwinsta = PsGetCurrentProcess()->Win32WindowStation;
|
|
}
|
|
pwinsta = NULL;
|
|
Status = ReferenceWindowStation(Thread, hwinsta,
|
|
WINSTA_ACCESSGLOBALATOMS, &pwinsta, TRUE);
|
|
if (NT_SUCCESS(Status)) {
|
|
try {
|
|
GlobalAtomTable = pwinsta->pGlobalAtomTable;
|
|
*ppGlobalAtomTable = pwinsta->pGlobalAtomTable;
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status) || GlobalAtomTable == NULL) {
|
|
RIPMSG3(RIP_ERROR,
|
|
"_UserGetGlobalAtomTable: NULL Atom Table (hwinsta=0x%X, pwinsta=0x%X, Status=0x%08X)",
|
|
hwinsta,
|
|
pwinsta,
|
|
Status);
|
|
}
|
|
#endif
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* _SetWindowStationUser
|
|
*
|
|
* Private API for winlogon to associate a windowstation with a user.
|
|
*
|
|
* History:
|
|
* 06-27-94 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
UINT _SetWindowStationUser(
|
|
PWINDOWSTATION pwinsta,
|
|
PLUID pluidUser,
|
|
PSID psidUser,
|
|
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 (psidUser != 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, psidUser, cbsidUser);
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
RIPERR1(ERROR_INVALID_PARAMETER,
|
|
RIP_WARNING,
|
|
"Invalid parameter \"psidUser\" (%#lx) to _SetWindowStationUser",
|
|
psidUser);
|
|
|
|
UserFreePool(pwinsta->psidUser);
|
|
pwinsta->psidUser = NULL;
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
pwinsta->psidUser = NULL;
|
|
}
|
|
|
|
pwinsta->luidUser = *pluidUser;
|
|
|
|
return TRUE;
|
|
}
|