/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Win32k subsystem * PURPOSE: Window stations * FILE: win32ss/user/ntuser/winsta.c * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net) * TODO: The process window station is created on * the first USER32/GDI32 call not related * to window station/desktop handling */ #include DBG_DEFAULT_CHANNEL(UserWinsta); /* GLOBALS *******************************************************************/ /* Currently active window station */ PWINSTATION_OBJECT InputWindowStation = NULL; /* Winlogon SAS window */ HWND hwndSAS = NULL; /* Full path to WindowStations directory */ UNICODE_STRING gustrWindowStationsDir; /* INITIALIZATION FUNCTIONS ****************************************************/ INIT_FUNCTION NTSTATUS NTAPI InitWindowStationImpl(VOID) { GENERIC_MAPPING IntWindowStationMapping = { WINSTA_READ, WINSTA_WRITE, WINSTA_EXECUTE, WINSTA_ACCESS_ALL}; /* Set Winsta Object Attributes */ ExWindowStationObjectType->TypeInfo.DefaultNonPagedPoolCharge = sizeof(WINSTATION_OBJECT); ExWindowStationObjectType->TypeInfo.GenericMapping = IntWindowStationMapping; ExWindowStationObjectType->TypeInfo.ValidAccessMask = WINSTA_ACCESS_ALL; return STATUS_SUCCESS; } NTSTATUS NTAPI UserCreateWinstaDirectory(VOID) { PPEB Peb; NTSTATUS Status; WCHAR wstrWindowStationsDir[MAX_PATH]; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE hWinstaDir; /* Create the WindowStations directory and cache its path for later use */ Peb = NtCurrentPeb(); if(Peb->SessionId == 0) { if (!RtlCreateUnicodeString(&gustrWindowStationsDir, WINSTA_OBJ_DIR)) { return STATUS_INSUFFICIENT_RESOURCES; } } else { swprintf(wstrWindowStationsDir, L"%ws\\%lu%ws", SESSION_DIR, Peb->SessionId, WINSTA_OBJ_DIR); if (!RtlCreateUnicodeString(&gustrWindowStationsDir, wstrWindowStationsDir)) { return STATUS_INSUFFICIENT_RESOURCES; } } InitializeObjectAttributes(&ObjectAttributes, &gustrWindowStationsDir, 0, NULL, NULL); Status = ZwCreateDirectoryObject(&hWinstaDir, 0, &ObjectAttributes); if (!NT_SUCCESS(Status)) { ERR("Could not create %wZ directory (Status 0x%X)\n", &gustrWindowStationsDir, Status); return Status; } TRACE("Created directory %wZ for session %lu\n", &gustrWindowStationsDir, Peb->SessionId); return Status; } /* OBJECT CALLBACKS **********************************************************/ NTSTATUS APIENTRY IntWinStaObjectDelete( _In_ PVOID Parameters) { PWIN32_DELETEMETHOD_PARAMETERS DeleteParameters = Parameters; PWINSTATION_OBJECT WinSta = (PWINSTATION_OBJECT)DeleteParameters->Object; TRACE("Deleting window station (0x%p)\n", WinSta); WinSta->Flags |= WSS_DYING; UserEmptyClipboardData(WinSta); RtlDestroyAtomTable(WinSta->AtomTable); RtlFreeUnicodeString(&WinSta->Name); return STATUS_SUCCESS; } NTSTATUS APIENTRY IntWinStaObjectParse( _In_ PVOID Parameters) { PWIN32_PARSEMETHOD_PARAMETERS ParseParameters = Parameters; PUNICODE_STRING RemainingName = ParseParameters->RemainingName; /* Assume we don't find anything */ *ParseParameters->Object = NULL; /* Check for an empty name */ if (!RemainingName->Length) { /* Make sure this is a window station, can't parse a desktop now */ if (ParseParameters->ObjectType != ExWindowStationObjectType) { /* Fail */ return STATUS_OBJECT_TYPE_MISMATCH; } /* Reference the window station and return */ ObReferenceObject(ParseParameters->ParseObject); *ParseParameters->Object = ParseParameters->ParseObject; return STATUS_SUCCESS; } /* Check for leading slash */ if (RemainingName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR) { /* Skip it */ RemainingName->Buffer++; RemainingName->Length -= sizeof(WCHAR); RemainingName->MaximumLength -= sizeof(WCHAR); } /* Check if there is still a slash */ if (wcschr(RemainingName->Buffer, OBJ_NAME_PATH_SEPARATOR)) { /* In this case, fail */ return STATUS_OBJECT_PATH_INVALID; } /* * Check if we are parsing a desktop. */ if (ParseParameters->ObjectType == ExDesktopObjectType) { /* Then call the desktop parse routine */ return IntDesktopObjectParse(ParseParameters->ParseObject, ParseParameters->ObjectType, ParseParameters->AccessState, ParseParameters->AccessMode, ParseParameters->Attributes, ParseParameters->CompleteName, RemainingName, ParseParameters->Context, ParseParameters->SecurityQos, ParseParameters->Object); } /* Should hopefully never get here */ return STATUS_OBJECT_TYPE_MISMATCH; } NTSTATUS NTAPI IntWinstaOkToClose( _In_ PVOID Parameters) { PWIN32_OKAYTOCLOSEMETHOD_PARAMETERS OkToCloseParameters = Parameters; PPROCESSINFO ppi; ppi = PsGetCurrentProcessWin32Process(); if(ppi && (OkToCloseParameters->Handle == ppi->hwinsta)) { return STATUS_ACCESS_DENIED; } return STATUS_SUCCESS; } /* PRIVATE FUNCTIONS **********************************************************/ /* * IntValidateWindowStationHandle * * Validates the window station handle. * * Remarks * If the function succeeds, the handle remains referenced. If the * fucntion fails, last error is set. */ NTSTATUS FASTCALL IntValidateWindowStationHandle( HWINSTA WindowStation, KPROCESSOR_MODE AccessMode, ACCESS_MASK DesiredAccess, PWINSTATION_OBJECT *Object, POBJECT_HANDLE_INFORMATION pObjectHandleInfo) { NTSTATUS Status; if (WindowStation == NULL) { ERR("Invalid window station handle\n"); EngSetLastError(ERROR_INVALID_HANDLE); return STATUS_INVALID_HANDLE; } Status = ObReferenceObjectByHandle(WindowStation, DesiredAccess, ExWindowStationObjectType, AccessMode, (PVOID*)Object, pObjectHandleInfo); if (!NT_SUCCESS(Status)) SetLastNtError(Status); return Status; } BOOL FASTCALL co_IntInitializeDesktopGraphics(VOID) { TEXTMETRICW tmw; UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"DISPLAY"); PDESKTOP pdesk; ScreenDeviceContext = IntGdiCreateDC(&DriverName, NULL, NULL, NULL, FALSE); if (NULL == ScreenDeviceContext) { IntDestroyPrimarySurface(); return FALSE; } GreSetDCOwner(ScreenDeviceContext, GDI_OBJ_HMGR_PUBLIC); if (! IntCreatePrimarySurface()) { return FALSE; } hSystemBM = NtGdiCreateCompatibleDC(ScreenDeviceContext); NtGdiSelectFont(hSystemBM, NtGdiGetStockObject(SYSTEM_FONT)); GreSetDCOwner(hSystemBM, GDI_OBJ_HMGR_PUBLIC); /* Update the SERVERINFO */ gpsi->aiSysMet[SM_CXSCREEN] = gppdevPrimary->gdiinfo.ulHorzRes; gpsi->aiSysMet[SM_CYSCREEN] = gppdevPrimary->gdiinfo.ulVertRes; gpsi->Planes = NtGdiGetDeviceCaps(ScreenDeviceContext, PLANES); gpsi->BitsPixel = NtGdiGetDeviceCaps(ScreenDeviceContext, BITSPIXEL); gpsi->BitCount = gpsi->Planes * gpsi->BitsPixel; gpsi->dmLogPixels = NtGdiGetDeviceCaps(ScreenDeviceContext, LOGPIXELSY); if (NtGdiGetDeviceCaps(ScreenDeviceContext, RASTERCAPS) & RC_PALETTE) { gpsi->PUSIFlags |= PUSIF_PALETTEDISPLAY; } else gpsi->PUSIFlags &= ~PUSIF_PALETTEDISPLAY; // Font is realized and this dc was previously set to internal DC_ATTR. gpsi->cxSysFontChar = IntGetCharDimensions(hSystemBM, &tmw, (DWORD*)&gpsi->cySysFontChar); gpsi->tmSysFont = tmw; /* Put the pointer in the center of the screen */ gpsi->ptCursor.x = gpsi->aiSysMet[SM_CXSCREEN] / 2; gpsi->ptCursor.y = gpsi->aiSysMet[SM_CYSCREEN] / 2; /* Attach monitor */ UserAttachMonitor((HDEV)gppdevPrimary); /* Setup the cursor */ co_IntLoadDefaultCursors(); /* Setup the icons */ co_IntSetWndIcons(); /* Setup Menu */ MenuInit(); /* Show the desktop */ pdesk = IntGetActiveDesktop(); ASSERT(pdesk); co_IntShowDesktop(pdesk, gpsi->aiSysMet[SM_CXSCREEN], gpsi->aiSysMet[SM_CYSCREEN], TRUE); return TRUE; } VOID FASTCALL IntEndDesktopGraphics(VOID) { if (NULL != ScreenDeviceContext) { // No need to allocate a new dcattr. GreSetDCOwner(ScreenDeviceContext, GDI_OBJ_HMGR_POWNED); GreDeleteObject(ScreenDeviceContext); ScreenDeviceContext = NULL; } IntHideDesktop(IntGetActiveDesktop()); IntDestroyPrimarySurface(); } HDC FASTCALL IntGetScreenDC(VOID) { return ScreenDeviceContext; } BOOL FASTCALL CheckWinstaAttributeAccess(ACCESS_MASK DesiredAccess) { PPROCESSINFO ppi = PsGetCurrentProcessWin32Process(); if ( gpidLogon != PsGetCurrentProcessId() ) { if (!(ppi->W32PF_flags & W32PF_IOWINSTA)) { ERR("Requires Interactive Window Station\n"); EngSetLastError(ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION); return FALSE; } if (!RtlAreAllAccessesGranted(ppi->amwinsta, DesiredAccess)) { ERR("Access Denied\n"); EngSetLastError(ERROR_ACCESS_DENIED); return FALSE; } } return TRUE; } /* PUBLIC FUNCTIONS ***********************************************************/ /* * NtUserCreateWindowStation * * Creates a new window station. * * Parameters * lpszWindowStationName * Pointer to a null-terminated string specifying the name of the * window station to be created. Window station names are * case-insensitive and cannot contain backslash characters (\). * Only members of the Administrators group are allowed to specify a * name. * * dwDesiredAccess * Requested type of access * * lpSecurity * Security descriptor * * Unknown3, Unknown4, Unknown5 * Unused * * Return Value * If the function succeeds, the return value is a handle to the newly * created window station. If the specified window station already * exists, the function succeeds and returns a handle to the existing * window station. If the function fails, the return value is NULL. * * Todo * Correct the prototype to match the Windows one (with 7 parameters * on Windows XP). * * Status * @implemented */ HWINSTA APIENTRY NtUserCreateWindowStation( POBJECT_ATTRIBUTES ObjectAttributes, ACCESS_MASK dwDesiredAccess, DWORD Unknown2, DWORD Unknown3, DWORD Unknown4, DWORD Unknown5, DWORD Unknown6) { UNICODE_STRING WindowStationName; PWINSTATION_OBJECT WindowStationObject; HWINSTA WindowStation; NTSTATUS Status; TRACE("NtUserCreateWindowStation called\n"); Status = ObOpenObjectByName(ObjectAttributes, ExWindowStationObjectType, UserMode, NULL, dwDesiredAccess, NULL, (PVOID*)&WindowStation); if (NT_SUCCESS(Status)) { TRACE("NtUserCreateWindowStation opened window station %wZ\n", ObjectAttributes->ObjectName); return (HWINSTA)WindowStation; } /* * No existing window station found, try to create new one */ /* Capture window station name */ _SEH2_TRY { ProbeForRead( ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), 1); Status = IntSafeCopyUnicodeStringTerminateNULL(&WindowStationName, ObjectAttributes->ObjectName); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status =_SEH2_GetExceptionCode(); } _SEH2_END if (! NT_SUCCESS(Status)) { ERR("Failed reading capturing window station name\n"); SetLastNtError(Status); return NULL; } /* Create the window station object */ Status = ObCreateObject(UserMode, ExWindowStationObjectType, ObjectAttributes, UserMode, NULL, sizeof(WINSTATION_OBJECT), 0, 0, (PVOID*)&WindowStationObject); if (!NT_SUCCESS(Status)) { ERR("ObCreateObject failed with %lx for window station %wZ\n", Status, &WindowStationName); ExFreePoolWithTag(WindowStationName.Buffer, TAG_STRING); SetLastNtError(STATUS_INSUFFICIENT_RESOURCES); return 0; } /* Initialize the window station */ RtlZeroMemory(WindowStationObject, sizeof(WINSTATION_OBJECT)); InitializeListHead(&WindowStationObject->DesktopListHead); WindowStationObject->Name = WindowStationName; WindowStationObject->dwSessionId = NtCurrentPeb()->SessionId; Status = RtlCreateAtomTable(37, &WindowStationObject->AtomTable); if (!NT_SUCCESS(Status)) { ERR("RtlCreateAtomTable failed with %lx for window station %wZ\n", Status, &WindowStationName); ObDereferenceObject(WindowStationObject); SetLastNtError(STATUS_INSUFFICIENT_RESOURCES); return 0; } Status = ObInsertObject((PVOID)WindowStationObject, NULL, dwDesiredAccess, 0, NULL, (PVOID*)&WindowStation); if (!NT_SUCCESS(Status)) { ERR("ObInsertObject failed with %lx for window station\n", Status); SetLastNtError(STATUS_INSUFFICIENT_RESOURCES); return 0; } if (InputWindowStation == NULL) { ERR("Initializing input window station\n"); InputWindowStation = WindowStationObject; WindowStationObject->Flags &= ~WSS_NOIO; InitCursorImpl(); } else { WindowStationObject->Flags |= WSS_NOIO; } TRACE("NtUserCreateWindowStation created object %p with name %wZ handle %p\n", WindowStation, &WindowStationObject->Name, WindowStation); return WindowStation; } /* * NtUserOpenWindowStation * * Opens an existing window station. * * Parameters * lpszWindowStationName * Name of the existing window station. * * dwDesiredAccess * Requested type of access. * * Return Value * If the function succeeds, the return value is the handle to the * specified window station. If the function fails, the return value * is NULL. * * Remarks * The returned handle can be closed with NtUserCloseWindowStation. * * Status * @implemented */ HWINSTA APIENTRY NtUserOpenWindowStation( POBJECT_ATTRIBUTES ObjectAttributes, ACCESS_MASK dwDesiredAccess) { HWINSTA hwinsta; NTSTATUS Status; Status = ObOpenObjectByName(ObjectAttributes, ExWindowStationObjectType, UserMode, NULL, dwDesiredAccess, NULL, (PVOID*)&hwinsta); if (!NT_SUCCESS(Status)) { ERR("NtUserOpenWindowStation failed\n"); SetLastNtError(Status); return 0; } TRACE("Opened window station %wZ with handle %p\n", ObjectAttributes->ObjectName, hwinsta); return hwinsta; } /* * NtUserCloseWindowStation * * Closes a window station handle. * * Parameters * hWinSta * Handle to the window station. * * Return Value * Status * * Remarks * The window station handle can be created with NtUserCreateWindowStation * or NtUserOpenWindowStation. Attemps to close a handle to the window * station assigned to the calling process will fail. * * Status * @implemented */ BOOL APIENTRY NtUserCloseWindowStation( HWINSTA hWinSta) { PWINSTATION_OBJECT Object; NTSTATUS Status; TRACE("NtUserCloseWindowStation called (%p)\n", hWinSta); if (hWinSta == UserGetProcessWindowStation()) { ERR("Attempted to close process window station\n"); return FALSE; } Status = IntValidateWindowStationHandle(hWinSta, UserMode, 0, &Object, 0); if (!NT_SUCCESS(Status)) { ERR("Validation of window station handle (%p) failed\n", hWinSta); return FALSE; } ObDereferenceObject(Object); TRACE("Closing window station handle (%p)\n", hWinSta); Status = ObCloseHandle(hWinSta, UserMode); if (!NT_SUCCESS(Status)) { SetLastNtError(Status); return FALSE; } return TRUE; } /* * NtUserGetObjectInformation * * The NtUserGetObjectInformation function retrieves information about a * window station or desktop object. * * Parameters * hObj * Handle to the window station or desktop object for which to * return information. This can be a handle of type HDESK or HWINSTA * (for example, a handle returned by NtUserCreateWindowStation, * NtUserOpenWindowStation, NtUserCreateDesktop, or NtUserOpenDesktop). * * nIndex * Specifies the object information to be retrieved. * * pvInfo * Pointer to a buffer to receive the object information. * * nLength * Specifies the size, in bytes, of the buffer pointed to by the * pvInfo parameter. * * lpnLengthNeeded * Pointer to a variable receiving the number of bytes required to * store the requested information. If this variable's value is * greater than the value of the nLength parameter when the function * returns, the function returns FALSE, and none of the information * is copied to the pvInfo buffer. If the value of the variable pointed * to by lpnLengthNeeded is less than or equal to the value of nLength, * the entire information block is copied. * * Return Value * If the function succeeds, the return value is nonzero. If the function * fails, the return value is zero. * * Status * @unimplemented */ BOOL APIENTRY NtUserGetObjectInformation( HANDLE hObject, DWORD nIndex, PVOID pvInformation, DWORD nLength, PDWORD nLengthNeeded) { NTSTATUS Status; PWINSTATION_OBJECT WinStaObject = NULL; PDESKTOP DesktopObject = NULL; USEROBJECTFLAGS ObjectFlags; PVOID pvData = NULL; DWORD nDataSize = 0; _SEH2_TRY { if (nLengthNeeded) ProbeForWrite(nLengthNeeded, sizeof(*nLengthNeeded), 1); ProbeForWrite(pvInformation, nLength, 1); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { SetLastNtError(_SEH2_GetExceptionCode()); return FALSE; } _SEH2_END; /* Try window station */ TRACE("Trying to open window station %p\n", hObject); Status = ObReferenceObjectByHandle(hObject, 0, ExWindowStationObjectType, UserMode, (PVOID*)&WinStaObject, NULL); if (Status == STATUS_OBJECT_TYPE_MISMATCH) { /* Try desktop */ TRACE("Trying to open desktop %p\n", hObject); WinStaObject = NULL; Status = IntValidateDesktopHandle(hObject, UserMode, 0, &DesktopObject); } if (!NT_SUCCESS(Status)) { ERR("Failed: 0x%x\n", Status); goto Exit; } TRACE("WinSta or Desktop opened!!\n"); /* Get data */ switch (nIndex) { case UOI_FLAGS: { /* This is a default implementation that does almost nothing */ ObjectFlags.fInherit = FALSE; ObjectFlags.fReserved = FALSE; ObjectFlags.dwFlags = 0; pvData = &ObjectFlags; nDataSize = sizeof(ObjectFlags); Status = STATUS_SUCCESS; ERR("UOI_FLAGS unimplemented!\n"); break; } case UOI_NAME: { if (WinStaObject != NULL) { pvData = WinStaObject->Name.Buffer; nDataSize = WinStaObject->Name.Length + sizeof(WCHAR); Status = STATUS_SUCCESS; } else if (DesktopObject != NULL) { pvData = DesktopObject->pDeskInfo->szDesktopName; nDataSize = (wcslen(DesktopObject->pDeskInfo->szDesktopName) + 1) * sizeof(WCHAR); Status = STATUS_SUCCESS; } else { Status = STATUS_INVALID_PARAMETER; } break; } case UOI_TYPE: { if (WinStaObject != NULL) { pvData = L"WindowStation"; nDataSize = sizeof(L"WindowStation"); Status = STATUS_SUCCESS; } else if (DesktopObject != NULL) { pvData = L"Desktop"; nDataSize = sizeof(L"Desktop"); Status = STATUS_SUCCESS; } else { Status = STATUS_INVALID_PARAMETER; } break; } case UOI_USER_SID: Status = STATUS_NOT_IMPLEMENTED; ERR("UOI_USER_SID unimplemented!\n"); break; default: Status = STATUS_INVALID_PARAMETER; break; } Exit: if ((Status == STATUS_SUCCESS) && (nLength < nDataSize)) Status = STATUS_BUFFER_TOO_SMALL; _SEH2_TRY { if (nLengthNeeded) *nLengthNeeded = nDataSize; /* Try to copy data to caller */ if (Status == STATUS_SUCCESS) { TRACE("Trying to copy data to caller (len = %lu, len needed = %lu)\n", nLength, nDataSize); RtlCopyMemory(pvInformation, pvData, nDataSize); } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; /* Release objects */ if (DesktopObject != NULL) ObDereferenceObject(DesktopObject); if (WinStaObject != NULL) ObDereferenceObject(WinStaObject); if (!NT_SUCCESS(Status)) { SetLastNtError(Status); return FALSE; } return TRUE; } /* * NtUserSetObjectInformation * * The NtUserSetObjectInformation function sets information about a * window station or desktop object. * * Parameters * hObj * Handle to the window station or desktop object for which to set * object information. This value can be a handle of type HDESK or * HWINSTA. * * nIndex * Specifies the object information to be set. * * pvInfo * Pointer to a buffer containing the object information. * * nLength * Specifies the size, in bytes, of the information contained in the * buffer pointed to by pvInfo. * * Return Value * If the function succeeds, the return value is nonzero. If the function * fails the return value is zero. * * Status * @unimplemented */ BOOL APIENTRY NtUserSetObjectInformation( HANDLE hObject, DWORD nIndex, PVOID pvInformation, DWORD nLength) { /* FIXME: ZwQueryObject */ /* FIXME: ZwSetInformationObject */ SetLastNtError(STATUS_UNSUCCESSFUL); return FALSE; } HWINSTA FASTCALL UserGetProcessWindowStation(VOID) { PPROCESSINFO ppi = PsGetCurrentProcessWin32Process(); return ppi->hwinsta; } /* * NtUserGetProcessWindowStation * * Returns a handle to the current process window station. * * Return Value * If the function succeeds, the return value is handle to the window * station assigned to the current process. If the function fails, the * return value is NULL. * * Status * @implemented */ HWINSTA APIENTRY NtUserGetProcessWindowStation(VOID) { return UserGetProcessWindowStation(); } BOOL FASTCALL UserSetProcessWindowStation(HWINSTA hWindowStation) { PPROCESSINFO ppi; NTSTATUS Status; HWINSTA hwinstaOld; OBJECT_HANDLE_INFORMATION ObjectHandleInfo; PWINSTATION_OBJECT NewWinSta = NULL, OldWinSta; ppi = PsGetCurrentProcessWin32Process(); /* Reference the new window station */ if(hWindowStation !=NULL) { Status = IntValidateWindowStationHandle(hWindowStation, UserMode, 0, &NewWinSta, &ObjectHandleInfo); if (!NT_SUCCESS(Status)) { TRACE("Validation of window station handle (%p) failed\n", hWindowStation); SetLastNtError(Status); return FALSE; } } OldWinSta = ppi->prpwinsta; hwinstaOld = PsGetProcessWin32WindowStation(ppi->peProcess); /* Dereference the previous window station */ if(OldWinSta != NULL) { ObDereferenceObject(OldWinSta); } /* Check if we have a stale handle (it should happen for console apps) */ if(hwinstaOld != ppi->hwinsta) { ObCloseHandle(hwinstaOld, UserMode); } /* * FIXME: Don't allow changing the window station if there are threads that are attached to desktops and own GUI objects. */ PsSetProcessWindowStation(ppi->peProcess, hWindowStation); ppi->prpwinsta = NewWinSta; ppi->hwinsta = hWindowStation; ppi->amwinsta = hWindowStation != NULL ? ObjectHandleInfo.GrantedAccess : 0; TRACE("WS : Granted Access 0x%08lx\n",ppi->amwinsta); if (RtlAreAllAccessesGranted(ppi->amwinsta, WINSTA_READSCREEN)) { ppi->W32PF_flags |= W32PF_READSCREENACCESSGRANTED; } else { ppi->W32PF_flags &= ~W32PF_READSCREENACCESSGRANTED; } if (NewWinSta && !(NewWinSta->Flags & WSS_NOIO) ) { ppi->W32PF_flags |= W32PF_IOWINSTA; } else // Might be closed if the handle is null. { ppi->W32PF_flags &= ~W32PF_IOWINSTA; } return TRUE; } /* * NtUserSetProcessWindowStation * * Assigns a window station to the current process. * * Parameters * hWinSta * Handle to the window station. * * Return Value * Status * * Status * @implemented */ BOOL APIENTRY NtUserSetProcessWindowStation(HWINSTA hWindowStation) { BOOL ret; UserEnterExclusive(); ret = UserSetProcessWindowStation(hWindowStation); UserLeave(); return ret; } /* * NtUserLockWindowStation * * Locks switching desktops. Only the logon application is allowed to call this function. * * Status * @implemented */ BOOL APIENTRY NtUserLockWindowStation(HWINSTA hWindowStation) { PWINSTATION_OBJECT Object; NTSTATUS Status; TRACE("About to set process window station with handle (%p)\n", hWindowStation); if (gpidLogon != PsGetCurrentProcessId()) { ERR("Unauthorized process attempted to lock the window station!\n"); EngSetLastError(ERROR_ACCESS_DENIED); return FALSE; } Status = IntValidateWindowStationHandle(hWindowStation, UserMode, 0, &Object, 0); if (!NT_SUCCESS(Status)) { TRACE("Validation of window station handle (%p) failed\n", hWindowStation); SetLastNtError(Status); return FALSE; } Object->Flags |= WSS_LOCKED; ObDereferenceObject(Object); return TRUE; } /* * NtUserUnlockWindowStation * * Unlocks switching desktops. Only the logon application is allowed to call this function. * * Status * @implemented */ BOOL APIENTRY NtUserUnlockWindowStation(HWINSTA hWindowStation) { PWINSTATION_OBJECT Object; NTSTATUS Status; BOOL Ret; TRACE("About to set process window station with handle (%p)\n", hWindowStation); if (gpidLogon != PsGetCurrentProcessId()) { ERR("Unauthorized process attempted to unlock the window station!\n"); EngSetLastError(ERROR_ACCESS_DENIED); return FALSE; } Status = IntValidateWindowStationHandle(hWindowStation, UserMode, 0, &Object, 0); if (!NT_SUCCESS(Status)) { TRACE("Validation of window station handle (%p) failed\n", hWindowStation); SetLastNtError(Status); return FALSE; } Ret = (Object->Flags & WSS_LOCKED) == WSS_LOCKED; Object->Flags &= ~WSS_LOCKED; ObDereferenceObject(Object); return Ret; } static NTSTATUS FASTCALL BuildWindowStationNameList( ULONG dwSize, PVOID lpBuffer, PULONG pRequiredSize) { OBJECT_ATTRIBUTES ObjectAttributes; NTSTATUS Status; HANDLE DirectoryHandle; char InitialBuffer[256], *Buffer; ULONG Context, ReturnLength, BufferSize; DWORD EntryCount; POBJECT_DIRECTORY_INFORMATION DirEntry; WCHAR NullWchar; /* * Try to open the directory. */ InitializeObjectAttributes(&ObjectAttributes, &gustrWindowStationsDir, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); Status = ZwOpenDirectoryObject(&DirectoryHandle, DIRECTORY_QUERY, &ObjectAttributes); if (!NT_SUCCESS(Status)) { return Status; } /* First try to query the directory using a fixed-size buffer */ Context = 0; Buffer = NULL; Status = ZwQueryDirectoryObject(DirectoryHandle, InitialBuffer, sizeof(InitialBuffer), FALSE, TRUE, &Context, &ReturnLength); if (NT_SUCCESS(Status)) { if (STATUS_NO_MORE_ENTRIES == ZwQueryDirectoryObject(DirectoryHandle, NULL, 0, FALSE, FALSE, &Context, NULL)) { /* Our fixed-size buffer is large enough */ Buffer = InitialBuffer; } } if (NULL == Buffer) { /* Need a larger buffer, check how large exactly */ Status = ZwQueryDirectoryObject(DirectoryHandle, NULL, 0, FALSE, TRUE, &Context, &ReturnLength); if (!NT_SUCCESS(Status)) { ERR("ZwQueryDirectoryObject failed\n"); ZwClose(DirectoryHandle); return Status; } BufferSize = ReturnLength; Buffer = ExAllocatePoolWithTag(PagedPool, BufferSize, TAG_WINSTA); if (NULL == Buffer) { ZwClose(DirectoryHandle); return STATUS_NO_MEMORY; } /* We should have a sufficiently large buffer now */ Context = 0; Status = ZwQueryDirectoryObject(DirectoryHandle, Buffer, BufferSize, FALSE, TRUE, &Context, &ReturnLength); if (! NT_SUCCESS(Status) || STATUS_NO_MORE_ENTRIES != ZwQueryDirectoryObject(DirectoryHandle, NULL, 0, FALSE, FALSE, &Context, NULL)) { /* Something went wrong, maybe someone added a directory entry? Just give up. */ ExFreePoolWithTag(Buffer, TAG_WINSTA); ZwClose(DirectoryHandle); return NT_SUCCESS(Status) ? STATUS_INTERNAL_ERROR : Status; } } ZwClose(DirectoryHandle); /* * Count the required size of buffer. */ ReturnLength = sizeof(DWORD); EntryCount = 0; for (DirEntry = (POBJECT_DIRECTORY_INFORMATION) Buffer; 0 != DirEntry->Name.Length; DirEntry++) { ReturnLength += DirEntry->Name.Length + sizeof(WCHAR); EntryCount++; } TRACE("Required size: %lu Entry count: %lu\n", ReturnLength, EntryCount); if (NULL != pRequiredSize) { Status = MmCopyToCaller(pRequiredSize, &ReturnLength, sizeof(ULONG)); if (! NT_SUCCESS(Status)) { if (Buffer != InitialBuffer) { ExFreePoolWithTag(Buffer, TAG_WINSTA); } return STATUS_BUFFER_TOO_SMALL; } } /* * Check if the supplied buffer is large enough. */ if (dwSize < ReturnLength) { if (Buffer != InitialBuffer) { ExFreePoolWithTag(Buffer, TAG_WINSTA); } return STATUS_BUFFER_TOO_SMALL; } /* * Generate the resulting buffer contents. */ Status = MmCopyToCaller(lpBuffer, &EntryCount, sizeof(DWORD)); if (! NT_SUCCESS(Status)) { if (Buffer != InitialBuffer) { ExFreePoolWithTag(Buffer, TAG_WINSTA); } return Status; } lpBuffer = (PVOID) ((PCHAR) lpBuffer + sizeof(DWORD)); NullWchar = L'\0'; for (DirEntry = (POBJECT_DIRECTORY_INFORMATION) Buffer; 0 != DirEntry->Name.Length; DirEntry++) { Status = MmCopyToCaller(lpBuffer, DirEntry->Name.Buffer, DirEntry->Name.Length); if (! NT_SUCCESS(Status)) { if (Buffer != InitialBuffer) { ExFreePoolWithTag(Buffer, TAG_WINSTA); } return Status; } lpBuffer = (PVOID) ((PCHAR) lpBuffer + DirEntry->Name.Length); Status = MmCopyToCaller(lpBuffer, &NullWchar, sizeof(WCHAR)); if (! NT_SUCCESS(Status)) { if (Buffer != InitialBuffer) { ExFreePoolWithTag(Buffer, TAG_WINSTA); } return Status; } lpBuffer = (PVOID) ((PCHAR) lpBuffer + sizeof(WCHAR)); } /* * Clean up */ if (Buffer != InitialBuffer) { ExFreePoolWithTag(Buffer, TAG_WINSTA); } return STATUS_SUCCESS; } static NTSTATUS FASTCALL BuildDesktopNameList( HWINSTA hWindowStation, ULONG dwSize, PVOID lpBuffer, PULONG pRequiredSize) { NTSTATUS Status; PWINSTATION_OBJECT WindowStation; PLIST_ENTRY DesktopEntry; PDESKTOP DesktopObject; DWORD EntryCount; ULONG ReturnLength; WCHAR NullWchar; UNICODE_STRING DesktopName; Status = IntValidateWindowStationHandle(hWindowStation, UserMode, 0, &WindowStation, 0); if (! NT_SUCCESS(Status)) { return Status; } /* * Count the required size of buffer. */ ReturnLength = sizeof(DWORD); EntryCount = 0; for (DesktopEntry = WindowStation->DesktopListHead.Flink; DesktopEntry != &WindowStation->DesktopListHead; DesktopEntry = DesktopEntry->Flink) { DesktopObject = CONTAINING_RECORD(DesktopEntry, DESKTOP, ListEntry); RtlInitUnicodeString(&DesktopName, DesktopObject->pDeskInfo->szDesktopName); ReturnLength += DesktopName.Length + sizeof(WCHAR); EntryCount++; } TRACE("Required size: %lu Entry count: %lu\n", ReturnLength, EntryCount); if (NULL != pRequiredSize) { Status = MmCopyToCaller(pRequiredSize, &ReturnLength, sizeof(ULONG)); if (! NT_SUCCESS(Status)) { ObDereferenceObject(WindowStation); return STATUS_BUFFER_TOO_SMALL; } } /* * Check if the supplied buffer is large enough. */ if (dwSize < ReturnLength) { ObDereferenceObject(WindowStation); return STATUS_BUFFER_TOO_SMALL; } /* * Generate the resulting buffer contents. */ Status = MmCopyToCaller(lpBuffer, &EntryCount, sizeof(DWORD)); if (! NT_SUCCESS(Status)) { ObDereferenceObject(WindowStation); return Status; } lpBuffer = (PVOID) ((PCHAR) lpBuffer + sizeof(DWORD)); NullWchar = L'\0'; for (DesktopEntry = WindowStation->DesktopListHead.Flink; DesktopEntry != &WindowStation->DesktopListHead; DesktopEntry = DesktopEntry->Flink) { DesktopObject = CONTAINING_RECORD(DesktopEntry, DESKTOP, ListEntry); RtlInitUnicodeString(&DesktopName, DesktopObject->pDeskInfo->szDesktopName); Status = MmCopyToCaller(lpBuffer, DesktopName.Buffer, DesktopName.Length); if (! NT_SUCCESS(Status)) { ObDereferenceObject(WindowStation); return Status; } lpBuffer = (PVOID) ((PCHAR)lpBuffer + DesktopName.Length); Status = MmCopyToCaller(lpBuffer, &NullWchar, sizeof(WCHAR)); if (! NT_SUCCESS(Status)) { ObDereferenceObject(WindowStation); return Status; } lpBuffer = (PVOID) ((PCHAR) lpBuffer + sizeof(WCHAR)); } /* * Clean up and return */ ObDereferenceObject(WindowStation); return STATUS_SUCCESS; } /* * NtUserBuildNameList * * Function used for enumeration of desktops or window stations. * * Parameters * hWinSta * For enumeration of window stations this parameter must be set to * zero. Otherwise it's handle for window station. * * dwSize * Size of buffer passed by caller. * * lpBuffer * Buffer passed by caller. If the function succeeds, the buffer is * filled with window station/desktop count (in first DWORD) and * NULL-terminated window station/desktop names. * * pRequiredSize * If the function succeeds, this is the number of bytes copied. * Otherwise it's size of buffer needed for function to succeed. * * Status * @implemented */ NTSTATUS APIENTRY NtUserBuildNameList( HWINSTA hWindowStation, ULONG dwSize, PVOID lpBuffer, PULONG pRequiredSize) { /* The WindowStation name list and desktop name list are build in completely different ways. Call the appropriate function */ return NULL == hWindowStation ? BuildWindowStationNameList(dwSize, lpBuffer, pRequiredSize) : BuildDesktopNameList(hWindowStation, dwSize, lpBuffer, pRequiredSize); } /* * @implemented */ BOOL APIENTRY NtUserSetLogonNotifyWindow(HWND hWnd) { if (gpidLogon != PsGetCurrentProcessId()) { return FALSE; } if (!IntIsWindow(hWnd)) { return FALSE; } hwndSAS = hWnd; return TRUE; } BOOL APIENTRY NtUserLockWorkStation(VOID) { BOOL ret; PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); UserEnterExclusive(); if (pti->rpdesk == IntGetActiveDesktop()) { ret = UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_LOCK_WORKSTATION, 0); } else { ret = FALSE; } UserLeave(); return ret; } BOOL APIENTRY NtUserSetWindowStationUser( HWINSTA hWindowStation, PLUID pluid, PSID psid, DWORD size) { NTSTATUS Status; PWINSTATION_OBJECT WindowStation = NULL; BOOL Ret = FALSE; UserEnterExclusive(); if (gpidLogon != PsGetCurrentProcessId()) { EngSetLastError(ERROR_ACCESS_DENIED); goto Leave; } Status = IntValidateWindowStationHandle(hWindowStation, UserMode, 0, &WindowStation, 0); if (!NT_SUCCESS(Status)) { goto Leave; } if (WindowStation->psidUser) { ExFreePoolWithTag(WindowStation->psidUser, USERTAG_SECURITY); } WindowStation->psidUser = ExAllocatePoolWithTag(PagedPool, size, USERTAG_SECURITY); if (WindowStation->psidUser == NULL) { EngSetLastError(ERROR_OUTOFMEMORY); goto Leave; } _SEH2_TRY { ProbeForRead( psid, size, 1); ProbeForRead( pluid, sizeof(LUID), 1); RtlCopyMemory(WindowStation->psidUser, psid, size); WindowStation->luidUser = *pluid; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(WindowStation->psidUser, USERTAG_SECURITY); WindowStation->psidUser = 0; goto Leave; } Ret = TRUE; Leave: if (WindowStation) ObDereferenceObject(WindowStation); UserLeave(); return Ret; } /* EOF */