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