xref: /reactos/win32ss/user/ntuser/winsta.c (revision 23373acb)
1 /*
2  *  COPYRIGHT:        See COPYING in the top level directory
3  *  PROJECT:          ReactOS Win32k subsystem
4  *  PURPOSE:          Window stations
5  *  FILE:             win32ss/user/ntuser/winsta.c
6  *  PROGRAMER:        Casper S. Hornstrup (chorns@users.sourceforge.net)
7  *  TODO:             The process window station is created on
8  *                    the first USER32/GDI32 call not related
9  *                    to window station/desktop handling
10  */
11 
12 #include <win32k.h>
13 DBG_DEFAULT_CHANNEL(UserWinsta);
14 
15 /* GLOBALS *******************************************************************/
16 
17 /*
18  * The currently active window station. This is the
19  * only one interactive window station on the system.
20  */
21 PWINSTATION_OBJECT InputWindowStation = NULL;
22 
23 /* Winlogon SAS window */
24 HWND hwndSAS = NULL;
25 
26 /* Full path to WindowStations directory */
27 UNICODE_STRING gustrWindowStationsDir;
28 
29 /* INITIALIZATION FUNCTIONS ****************************************************/
30 
31 INIT_FUNCTION
32 NTSTATUS
33 NTAPI
34 InitWindowStationImpl(VOID)
35 {
36     GENERIC_MAPPING IntWindowStationMapping = { WINSTA_READ,
37                                                 WINSTA_WRITE,
38                                                 WINSTA_EXECUTE,
39                                                 WINSTA_ACCESS_ALL};
40 
41     /* Set Winsta Object Attributes */
42     ExWindowStationObjectType->TypeInfo.DefaultNonPagedPoolCharge = sizeof(WINSTATION_OBJECT);
43     ExWindowStationObjectType->TypeInfo.GenericMapping = IntWindowStationMapping;
44     ExWindowStationObjectType->TypeInfo.ValidAccessMask = WINSTA_ACCESS_ALL;
45 
46     return STATUS_SUCCESS;
47 }
48 
49 NTSTATUS
50 NTAPI
51 UserCreateWinstaDirectory(VOID)
52 {
53     NTSTATUS Status;
54     PPEB Peb;
55     OBJECT_ATTRIBUTES ObjectAttributes;
56     HANDLE hWinstaDir;
57     WCHAR wstrWindowStationsDir[MAX_PATH];
58 
59     /* Create the WindowStations directory and cache its path for later use */
60     Peb = NtCurrentPeb();
61     if(Peb->SessionId == 0)
62     {
63         if (!RtlCreateUnicodeString(&gustrWindowStationsDir, WINSTA_OBJ_DIR))
64         {
65             return STATUS_INSUFFICIENT_RESOURCES;
66         }
67     }
68     else
69     {
70         Status = RtlStringCbPrintfW(wstrWindowStationsDir,
71                                     sizeof(wstrWindowStationsDir),
72                                     L"%ws\\%lu%ws",
73                                     SESSION_DIR,
74                                     Peb->SessionId,
75                                     WINSTA_OBJ_DIR);
76         if (!NT_SUCCESS(Status))
77             return Status;
78 
79         if (!RtlCreateUnicodeString(&gustrWindowStationsDir, wstrWindowStationsDir))
80         {
81             return STATUS_INSUFFICIENT_RESOURCES;
82         }
83     }
84 
85     InitializeObjectAttributes(&ObjectAttributes,
86                                &gustrWindowStationsDir,
87                                OBJ_KERNEL_HANDLE,
88                                NULL,
89                                NULL);
90     Status = ZwCreateDirectoryObject(&hWinstaDir, DIRECTORY_CREATE_OBJECT, &ObjectAttributes);
91     if (!NT_SUCCESS(Status))
92     {
93         ERR("Could not create %wZ directory (Status 0x%X)\n", &gustrWindowStationsDir,  Status);
94         return Status;
95     }
96 
97     TRACE("Created directory %wZ for session %lu\n", &gustrWindowStationsDir, Peb->SessionId);
98 
99     return Status;
100 }
101 
102 /* OBJECT CALLBACKS ***********************************************************/
103 
104 NTSTATUS
105 NTAPI
106 IntWinStaObjectDelete(
107     _In_ PVOID Parameters)
108 {
109     PWIN32_DELETEMETHOD_PARAMETERS DeleteParameters = Parameters;
110     PWINSTATION_OBJECT WinSta = (PWINSTATION_OBJECT)DeleteParameters->Object;
111 
112     TRACE("Deleting window station 0x%p\n", WinSta);
113 
114     if (WinSta == InputWindowStation)
115     {
116         ERR("WARNING: Deleting the interactive window station '%wZ'!\n",
117             &(OBJECT_HEADER_TO_NAME_INFO(OBJECT_TO_OBJECT_HEADER(InputWindowStation))->Name));
118 
119         /* Only Winlogon can close and delete the interactive window station */
120         ASSERT(gpidLogon == PsGetCurrentProcessId());
121 
122         InputWindowStation = NULL;
123     }
124 
125     WinSta->Flags |= WSS_DYING;
126 
127     UserEmptyClipboardData(WinSta);
128 
129     RtlDestroyAtomTable(WinSta->AtomTable);
130 
131     return STATUS_SUCCESS;
132 }
133 
134 NTSTATUS
135 NTAPI
136 IntWinStaObjectParse(
137     _In_ PVOID Parameters)
138 {
139     PWIN32_PARSEMETHOD_PARAMETERS ParseParameters = Parameters;
140     PUNICODE_STRING RemainingName = ParseParameters->RemainingName;
141 
142     /* Assume we don't find anything */
143     *ParseParameters->Object = NULL;
144 
145     /* Check for an empty name */
146     if (!RemainingName->Length)
147     {
148         /* Make sure this is a window station, can't parse a desktop now */
149         if (ParseParameters->ObjectType != ExWindowStationObjectType)
150         {
151             /* Fail */
152             return STATUS_OBJECT_TYPE_MISMATCH;
153         }
154 
155         /* Reference the window station and return */
156         ObReferenceObject(ParseParameters->ParseObject);
157         *ParseParameters->Object = ParseParameters->ParseObject;
158         return STATUS_SUCCESS;
159     }
160 
161     /* Check for leading slash */
162     if (RemainingName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
163     {
164         /* Skip it */
165         RemainingName->Buffer++;
166         RemainingName->Length -= sizeof(WCHAR);
167         RemainingName->MaximumLength -= sizeof(WCHAR);
168     }
169 
170     /* Check if there is still a slash */
171     if (wcschr(RemainingName->Buffer, OBJ_NAME_PATH_SEPARATOR))
172     {
173         /* In this case, fail */
174         return STATUS_OBJECT_PATH_INVALID;
175     }
176 
177     /*
178      * Check if we are parsing a desktop.
179      */
180     if (ParseParameters->ObjectType == ExDesktopObjectType)
181     {
182         /* Then call the desktop parse routine */
183         return IntDesktopObjectParse(ParseParameters->ParseObject,
184                                      ParseParameters->ObjectType,
185                                      ParseParameters->AccessState,
186                                      ParseParameters->AccessMode,
187                                      ParseParameters->Attributes,
188                                      ParseParameters->CompleteName,
189                                      RemainingName,
190                                      ParseParameters->Context,
191                                      ParseParameters->SecurityQos,
192                                      ParseParameters->Object);
193     }
194 
195     /* Should hopefully never get here */
196     return STATUS_OBJECT_TYPE_MISMATCH;
197 }
198 
199 NTSTATUS
200 NTAPI
201 IntWinStaOkToClose(
202     _In_ PVOID Parameters)
203 {
204     PWIN32_OKAYTOCLOSEMETHOD_PARAMETERS OkToCloseParameters = Parameters;
205     PPROCESSINFO ppi;
206 
207     ppi = PsGetCurrentProcessWin32Process();
208 
209     if (ppi && (OkToCloseParameters->Handle == ppi->hwinsta))
210     {
211         return STATUS_ACCESS_DENIED;
212     }
213 
214     return STATUS_SUCCESS;
215 }
216 
217 /* PRIVATE FUNCTIONS **********************************************************/
218 
219 /*
220  * IntValidateWindowStationHandle
221  *
222  * Validates the window station handle.
223  *
224  * Remarks
225  *    If the function succeeds, the handle remains referenced. If the
226  *    fucntion fails, last error is set.
227  */
228 
229 NTSTATUS FASTCALL
230 IntValidateWindowStationHandle(
231     HWINSTA WindowStation,
232     KPROCESSOR_MODE AccessMode,
233     ACCESS_MASK DesiredAccess,
234     PWINSTATION_OBJECT *Object,
235     POBJECT_HANDLE_INFORMATION pObjectHandleInfo)
236 {
237     NTSTATUS Status;
238 
239     if (WindowStation == NULL)
240     {
241         ERR("Invalid window station handle\n");
242         EngSetLastError(ERROR_INVALID_HANDLE);
243         return STATUS_INVALID_HANDLE;
244     }
245 
246     Status = ObReferenceObjectByHandle(WindowStation,
247                                        DesiredAccess,
248                                        ExWindowStationObjectType,
249                                        AccessMode,
250                                        (PVOID*)Object,
251                                        pObjectHandleInfo);
252 
253     if (!NT_SUCCESS(Status))
254         SetLastNtError(Status);
255 
256     return Status;
257 }
258 
259 BOOL FASTCALL
260 co_IntInitializeDesktopGraphics(VOID)
261 {
262     TEXTMETRICW tmw;
263     UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"DISPLAY");
264     PDESKTOP pdesk;
265 
266     ScreenDeviceContext = IntGdiCreateDC(&DriverName, NULL, NULL, NULL, FALSE);
267     if (NULL == ScreenDeviceContext)
268     {
269         IntDestroyPrimarySurface();
270         return FALSE;
271     }
272     GreSetDCOwner(ScreenDeviceContext, GDI_OBJ_HMGR_PUBLIC);
273 
274     if (!IntCreatePrimarySurface())
275     {
276         return FALSE;
277     }
278 
279     hSystemBM = NtGdiCreateCompatibleDC(ScreenDeviceContext);
280 
281     NtGdiSelectFont(hSystemBM, NtGdiGetStockObject(SYSTEM_FONT));
282     GreSetDCOwner(hSystemBM, GDI_OBJ_HMGR_PUBLIC);
283 
284     /* Update the system metrics */
285     InitMetrics();
286 
287     /* Set new size of the monitor */
288     UserUpdateMonitorSize((HDEV)gppdevPrimary);
289 
290     /* Update the SERVERINFO */
291     gpsi->aiSysMet[SM_CXSCREEN] = gppdevPrimary->gdiinfo.ulHorzRes;
292     gpsi->aiSysMet[SM_CYSCREEN] = gppdevPrimary->gdiinfo.ulVertRes;
293     gpsi->Planes        = NtGdiGetDeviceCaps(ScreenDeviceContext, PLANES);
294     gpsi->BitsPixel     = NtGdiGetDeviceCaps(ScreenDeviceContext, BITSPIXEL);
295     gpsi->BitCount      = gpsi->Planes * gpsi->BitsPixel;
296     gpsi->dmLogPixels   = NtGdiGetDeviceCaps(ScreenDeviceContext, LOGPIXELSY);
297     if (NtGdiGetDeviceCaps(ScreenDeviceContext, RASTERCAPS) & RC_PALETTE)
298     {
299         gpsi->PUSIFlags |= PUSIF_PALETTEDISPLAY;
300     }
301     else
302     {
303         gpsi->PUSIFlags &= ~PUSIF_PALETTEDISPLAY;
304     }
305     // Font is realized and this dc was previously set to internal DC_ATTR.
306     gpsi->cxSysFontChar = IntGetCharDimensions(hSystemBM, &tmw, (DWORD*)&gpsi->cySysFontChar);
307     gpsi->tmSysFont     = tmw;
308 
309     /* Put the pointer in the center of the screen */
310     gpsi->ptCursor.x = gpsi->aiSysMet[SM_CXSCREEN] / 2;
311     gpsi->ptCursor.y = gpsi->aiSysMet[SM_CYSCREEN] / 2;
312 
313     /* Attach monitor */
314     UserAttachMonitor((HDEV)gppdevPrimary);
315 
316     /* Setup the cursor */
317     co_IntLoadDefaultCursors();
318 
319     /* Setup the icons */
320     co_IntSetWndIcons();
321 
322     /* Setup Menu */
323     MenuInit();
324 
325     /* Show the desktop */
326     pdesk = IntGetActiveDesktop();
327     ASSERT(pdesk);
328     co_IntShowDesktop(pdesk, gpsi->aiSysMet[SM_CXSCREEN], gpsi->aiSysMet[SM_CYSCREEN], TRUE);
329 
330     return TRUE;
331 }
332 
333 VOID FASTCALL
334 IntEndDesktopGraphics(VOID)
335 {
336     if (NULL != ScreenDeviceContext)
337     {  // No need to allocate a new dcattr.
338         GreSetDCOwner(ScreenDeviceContext, GDI_OBJ_HMGR_POWNED);
339         GreDeleteObject(ScreenDeviceContext);
340         ScreenDeviceContext = NULL;
341     }
342     IntHideDesktop(IntGetActiveDesktop());
343     IntDestroyPrimarySurface();
344 }
345 
346 HDC FASTCALL
347 IntGetScreenDC(VOID)
348 {
349     return ScreenDeviceContext;
350 }
351 
352 BOOL FASTCALL
353 CheckWinstaAttributeAccess(ACCESS_MASK DesiredAccess)
354 {
355     PPROCESSINFO ppi = PsGetCurrentProcessWin32Process();
356     if ( gpidLogon != PsGetCurrentProcessId() )
357     {
358         if (!(ppi->W32PF_flags & W32PF_IOWINSTA))
359         {
360             ERR("Requires Interactive Window Station\n");
361             EngSetLastError(ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION);
362             return FALSE;
363         }
364         if (!RtlAreAllAccessesGranted(ppi->amwinsta, DesiredAccess))
365         {
366             ERR("Access Denied\n");
367             EngSetLastError(ERROR_ACCESS_DENIED);
368             return FALSE;
369         }
370     }
371     return TRUE;
372 }
373 
374 
375 /* PUBLIC FUNCTIONS ***********************************************************/
376 
377 /*
378  * NtUserCreateWindowStation
379  *
380  * Creates a new window station.
381  *
382  * Parameters
383  *    lpszWindowStationName
384  *       Pointer to a null-terminated string specifying the name of the
385  *       window station to be created. Window station names are
386  *       case-insensitive and cannot contain backslash characters (\).
387  *       Only members of the Administrators group are allowed to specify a
388  *       name.
389  *
390  *    dwDesiredAccess
391  *       Requested type of access
392  *
393  *    lpSecurity
394  *       Security descriptor
395  *
396  *    Unknown3, Unknown4, Unknown5, Unknown6
397  *       Unused
398  *
399  * Return Value
400  *    If the function succeeds, the return value is a handle to the newly
401  *    created window station. If the specified window station already
402  *    exists, the function succeeds and returns a handle to the existing
403  *    window station. If the function fails, the return value is NULL.
404  *
405  * Status
406  *    @implemented
407  */
408 
409 NTSTATUS
410 FASTCALL
411 IntCreateWindowStation(
412     OUT HWINSTA* phWinSta,
413     IN POBJECT_ATTRIBUTES ObjectAttributes,
414     IN KPROCESSOR_MODE AccessMode,
415     IN KPROCESSOR_MODE OwnerMode,
416     IN ACCESS_MASK dwDesiredAccess,
417     DWORD Unknown2,
418     DWORD Unknown3,
419     DWORD Unknown4,
420     DWORD Unknown5,
421     DWORD Unknown6)
422 {
423     NTSTATUS Status;
424     HWINSTA hWinSta;
425     PWINSTATION_OBJECT WindowStation;
426 
427     TRACE("IntCreateWindowStation called\n");
428 
429     ASSERT(phWinSta);
430     *phWinSta = NULL;
431 
432     Status = ObOpenObjectByName(ObjectAttributes,
433                                 ExWindowStationObjectType,
434                                 AccessMode,
435                                 NULL,
436                                 dwDesiredAccess,
437                                 NULL,
438                                 (PVOID*)&hWinSta);
439     if (NT_SUCCESS(Status))
440     {
441         TRACE("IntCreateWindowStation opened window station '%wZ'\n",
442               ObjectAttributes->ObjectName);
443         *phWinSta = hWinSta;
444         return Status;
445     }
446 
447     /*
448      * No existing window station found, try to create a new one.
449      */
450 
451     /* Create the window station object */
452     Status = ObCreateObject(AccessMode,
453                             ExWindowStationObjectType,
454                             ObjectAttributes,
455                             OwnerMode,
456                             NULL,
457                             sizeof(WINSTATION_OBJECT),
458                             0,
459                             0,
460                             (PVOID*)&WindowStation);
461     if (!NT_SUCCESS(Status))
462     {
463         ERR("ObCreateObject failed for window station '%wZ', Status 0x%08lx\n",
464             ObjectAttributes->ObjectName, Status);
465         SetLastNtError(Status);
466         return Status;
467     }
468 
469     /* Initialize the window station */
470     RtlZeroMemory(WindowStation, sizeof(WINSTATION_OBJECT));
471 
472     InitializeListHead(&WindowStation->DesktopListHead);
473     WindowStation->dwSessionId = NtCurrentPeb()->SessionId;
474     Status = RtlCreateAtomTable(37, &WindowStation->AtomTable);
475     if (!NT_SUCCESS(Status))
476     {
477         ERR("RtlCreateAtomTable failed for window station '%wZ', Status 0x%08lx\n",
478             ObjectAttributes->ObjectName, Status);
479         ObDereferenceObject(WindowStation);
480         SetLastNtError(Status);
481         return Status;
482     }
483 
484     Status = ObInsertObject(WindowStation,
485                             NULL,
486                             dwDesiredAccess,
487                             0,
488                             NULL,
489                             (PVOID*)&hWinSta);
490     if (!NT_SUCCESS(Status))
491     {
492         ERR("ObInsertObject failed for window station, Status 0x%08lx\n", Status);
493         SetLastNtError(Status);
494         return Status;
495     }
496 
497     // FIXME! TODO: Add this new window station to a linked list
498 
499     if (InputWindowStation == NULL)
500     {
501         ERR("Initializing input window station\n");
502 
503         /* Only Winlogon can create the interactive window station */
504         ASSERT(gpidLogon == PsGetCurrentProcessId());
505 
506         InputWindowStation = WindowStation;
507         WindowStation->Flags &= ~WSS_NOIO;
508 
509         InitCursorImpl();
510 
511         UserCreateSystemThread(ST_DESKTOP_THREAD);
512         UserCreateSystemThread(ST_RIT);
513 
514         /* Desktop functions require the desktop thread running so wait for it to initialize */
515         UserLeaveCo();
516         KeWaitForSingleObject(gpDesktopThreadStartedEvent,
517                               UserRequest,
518                               UserMode,
519                               FALSE,
520                               NULL);
521         UserEnterCo();
522     }
523     else
524     {
525         WindowStation->Flags |= WSS_NOIO;
526     }
527 
528     TRACE("IntCreateWindowStation created window station '%wZ' object 0x%p handle 0x%p\n",
529           ObjectAttributes->ObjectName, WindowStation, hWinSta);
530 
531     *phWinSta = hWinSta;
532     EngSetLastError(ERROR_SUCCESS);
533 
534     return STATUS_SUCCESS;
535 }
536 
537 static VOID
538 FreeUserModeWindowStationName(
539     IN OUT PUNICODE_STRING WindowStationName,
540     IN PUNICODE_STRING TebStaticUnicodeString,
541     IN OUT POBJECT_ATTRIBUTES UserModeObjectAttributes OPTIONAL,
542     IN POBJECT_ATTRIBUTES LocalObjectAttributes OPTIONAL)
543 {
544     SIZE_T MemSize = 0;
545 
546     /* Try to restore the user's UserModeObjectAttributes */
547     if (UserModeObjectAttributes && LocalObjectAttributes)
548     {
549         _SEH2_TRY
550         {
551             ProbeForWrite(UserModeObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG));
552             *UserModeObjectAttributes = *LocalObjectAttributes;
553         }
554         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
555         {
556             NOTHING;
557         }
558         _SEH2_END;
559     }
560 
561     /* Free the user-mode memory */
562     if (WindowStationName && (WindowStationName != TebStaticUnicodeString))
563     {
564         ZwFreeVirtualMemory(ZwCurrentProcess(),
565                             (PVOID*)&WindowStationName,
566                             &MemSize,
567                             MEM_RELEASE);
568     }
569 }
570 
571 static NTSTATUS
572 BuildUserModeWindowStationName(
573     IN OUT POBJECT_ATTRIBUTES UserModeObjectAttributes,
574     IN OUT POBJECT_ATTRIBUTES LocalObjectAttributes,
575     OUT PUNICODE_STRING* WindowStationName,
576     OUT PUNICODE_STRING* TebStaticUnicodeString)
577 {
578     NTSTATUS Status;
579     SIZE_T MemSize;
580 
581     LUID CallerLuid;
582     PTEB Teb;
583     USHORT StrSize;
584 
585     *WindowStationName = NULL;
586     *TebStaticUnicodeString = NULL;
587 
588     /* Retrieve the current process LUID */
589     Status = GetProcessLuid(NULL, NULL, &CallerLuid);
590     if (!NT_SUCCESS(Status))
591     {
592         ERR("Failed to retrieve the caller LUID, Status 0x%08lx\n", Status);
593         return Status;
594     }
595 
596     /* Compute the needed string size */
597     MemSize = _scwprintf(L"%wZ\\Service-0x%x-%x$",
598                          &gustrWindowStationsDir,
599                          CallerLuid.HighPart,
600                          CallerLuid.LowPart);
601     MemSize = MemSize * sizeof(WCHAR) + sizeof(UNICODE_NULL);
602     if (MemSize > MAXUSHORT)
603     {
604         ERR("Window station name length is too long.\n");
605         return STATUS_NAME_TOO_LONG;
606     }
607     StrSize = (USHORT)MemSize;
608 
609     /*
610      * Check whether it's short enough so that we can use the static buffer
611      * in the TEB. Otherwise continue with virtual memory allocation.
612      */
613     Teb = NtCurrentTeb();
614     if (Teb && (StrSize <= sizeof(Teb->StaticUnicodeBuffer)))
615     {
616         /* We can use the TEB's static unicode string */
617         ASSERT(Teb->StaticUnicodeString.Buffer == Teb->StaticUnicodeBuffer);
618         ASSERT(Teb->StaticUnicodeString.MaximumLength == sizeof(Teb->StaticUnicodeBuffer));
619 
620         /* Remember the TEB's static unicode string address for later */
621         *TebStaticUnicodeString = &Teb->StaticUnicodeString;
622 
623         *WindowStationName = *TebStaticUnicodeString;
624         (*WindowStationName)->Length = 0;
625     }
626     else
627     {
628         /* The TEB's static unicode string is too small, allocate some user-mode virtual memory */
629         MemSize += ALIGN_UP(sizeof(UNICODE_STRING), sizeof(PVOID));
630 
631         /* Allocate the memory in user-mode */
632         Status = ZwAllocateVirtualMemory(ZwCurrentProcess(),
633                                          (PVOID*)WindowStationName,
634                                          0,
635                                          &MemSize,
636                                          MEM_COMMIT,
637                                          PAGE_READWRITE);
638         if (!NT_SUCCESS(Status))
639         {
640             ERR("ZwAllocateVirtualMemory() failed, Status 0x%08lx\n", Status);
641             return Status;
642         }
643 
644         RtlInitEmptyUnicodeString(*WindowStationName,
645                                   (PWCHAR)((ULONG_PTR)*WindowStationName +
646                                       ALIGN_UP(sizeof(UNICODE_STRING), sizeof(PVOID))),
647                                   StrSize);
648     }
649 
650     /* Build a valid window station name from the LUID */
651     Status = RtlStringCbPrintfW((*WindowStationName)->Buffer,
652                                 (*WindowStationName)->MaximumLength,
653                                 L"%wZ\\Service-0x%x-%x$",
654                                 &gustrWindowStationsDir,
655                                 CallerLuid.HighPart,
656                                 CallerLuid.LowPart);
657     if (!NT_SUCCESS(Status))
658     {
659         ERR("Impossible to build a valid window station name, Status 0x%08lx\n", Status);
660         goto Quit;
661     }
662     (*WindowStationName)->Length = (USHORT)(wcslen((*WindowStationName)->Buffer) * sizeof(WCHAR));
663 
664     /* Try to update the user's UserModeObjectAttributes */
665     _SEH2_TRY
666     {
667         ProbeForWrite(UserModeObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG));
668         *LocalObjectAttributes = *UserModeObjectAttributes;
669 
670         UserModeObjectAttributes->ObjectName = *WindowStationName;
671         UserModeObjectAttributes->RootDirectory = NULL;
672 
673         Status = STATUS_SUCCESS;
674     }
675     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
676     {
677         Status = _SEH2_GetExceptionCode();
678     }
679     _SEH2_END;
680 
681 Quit:
682     if (!NT_SUCCESS(Status))
683     {
684         /* Release the window station name */
685         FreeUserModeWindowStationName(*WindowStationName,
686                                       *TebStaticUnicodeString,
687                                       NULL, NULL);
688     }
689 
690     return Status;
691 }
692 
693 HWINSTA
694 APIENTRY
695 NtUserCreateWindowStation(
696     IN POBJECT_ATTRIBUTES ObjectAttributes,
697     IN ACCESS_MASK dwDesiredAccess,
698     DWORD Unknown2,
699     DWORD Unknown3,
700     DWORD Unknown4,
701     DWORD Unknown5,
702     DWORD Unknown6)
703 {
704     NTSTATUS Status = STATUS_SUCCESS;
705     HWINSTA hWinSta = NULL;
706     OBJECT_ATTRIBUTES LocalObjectAttributes;
707     PUNICODE_STRING WindowStationName = NULL;
708     PUNICODE_STRING TebStaticUnicodeString = NULL;
709     KPROCESSOR_MODE OwnerMode = UserMode;
710 
711     TRACE("NtUserCreateWindowStation called\n");
712 
713     /* Capture the object attributes and the window station name */
714     _SEH2_TRY
715     {
716         ProbeForRead(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG));
717         LocalObjectAttributes = *ObjectAttributes;
718         if (LocalObjectAttributes.Length != sizeof(OBJECT_ATTRIBUTES))
719         {
720             ERR("Invalid ObjectAttributes length!\n");
721             Status = STATUS_INVALID_PARAMETER;
722             _SEH2_LEAVE;
723         }
724 
725         /*
726          * Check whether the caller provided a window station name together
727          * with a RootDirectory handle.
728          *
729          * If the caller did not provide a window station name, build a new one
730          * based on the logon session identifier for the calling process.
731          * The new name is allocated in user-mode, as the rest of ObjectAttributes
732          * already is, so that the validation performed by the Object Manager
733          * can be done adequately.
734          */
735         if ((LocalObjectAttributes.ObjectName == NULL ||
736              LocalObjectAttributes.ObjectName->Buffer == NULL ||
737              LocalObjectAttributes.ObjectName->Length == 0 ||
738              LocalObjectAttributes.ObjectName->Buffer[0] == UNICODE_NULL)
739             /* &&
740             LocalObjectAttributes.RootDirectory == NULL */)
741         {
742             /* No, build the new window station name */
743             Status = BuildUserModeWindowStationName(ObjectAttributes,
744                                                     &LocalObjectAttributes,
745                                                     &WindowStationName,
746                                                     &TebStaticUnicodeString);
747             if (!NT_SUCCESS(Status))
748             {
749                 ERR("BuildUserModeWindowStationName() failed, Status 0x%08lx\n", Status);
750                 _SEH2_LEAVE;
751             }
752             OwnerMode = KernelMode;
753         }
754     }
755     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
756     {
757         Status =_SEH2_GetExceptionCode();
758         ERR("ObjectAttributes capture failed, Status 0x%08lx\n", Status);
759     }
760     _SEH2_END;
761 
762     if (!NT_SUCCESS(Status))
763     {
764         SetLastNtError(Status);
765         return NULL;
766     }
767 
768     UserEnterExclusive();
769 
770     /* Create the window station */
771     Status = IntCreateWindowStation(&hWinSta,
772                                     ObjectAttributes,
773                                     UserMode,
774                                     OwnerMode,
775                                     dwDesiredAccess,
776                                     Unknown2,
777                                     Unknown3,
778                                     Unknown4,
779                                     Unknown5,
780                                     Unknown6);
781     UserLeave();
782 
783     if (NT_SUCCESS(Status))
784     {
785         TRACE("NtUserCreateWindowStation created window station '%wZ' with handle 0x%p\n",
786               ObjectAttributes->ObjectName, hWinSta);
787     }
788     else
789     {
790         ASSERT(hWinSta == NULL);
791         ERR("NtUserCreateWindowStation failed to create window station '%wZ', Status 0x%08lx\n",
792             ObjectAttributes->ObjectName, Status);
793     }
794 
795     /* Try to restore the user's ObjectAttributes and release the window station name */
796     FreeUserModeWindowStationName(WindowStationName,
797                                   TebStaticUnicodeString,
798                                   (OwnerMode == KernelMode ? ObjectAttributes : NULL),
799                                   &LocalObjectAttributes);
800 
801     if (!NT_SUCCESS(Status))
802     {
803         ASSERT(hWinSta == NULL);
804         SetLastNtError(Status);
805     }
806 
807     return hWinSta;
808 }
809 
810 /*
811  * NtUserOpenWindowStation
812  *
813  * Opens an existing window station.
814  *
815  * Parameters
816  *    lpszWindowStationName
817  *       Name of the existing window station.
818  *
819  *    dwDesiredAccess
820  *       Requested type of access.
821  *
822  * Return Value
823  *    If the function succeeds, the return value is the handle to the
824  *    specified window station. If the function fails, the return value
825  *    is NULL.
826  *
827  * Remarks
828  *    The returned handle can be closed with NtUserCloseWindowStation.
829  *
830  * Status
831  *    @implemented
832  */
833 
834 HWINSTA
835 APIENTRY
836 NtUserOpenWindowStation(
837     IN POBJECT_ATTRIBUTES ObjectAttributes,
838     IN ACCESS_MASK dwDesiredAccess)
839 {
840     NTSTATUS Status = STATUS_SUCCESS;
841     HWINSTA hWinSta = NULL;
842     OBJECT_ATTRIBUTES LocalObjectAttributes;
843     PUNICODE_STRING WindowStationName = NULL;
844     PUNICODE_STRING TebStaticUnicodeString = NULL;
845     KPROCESSOR_MODE OwnerMode = UserMode;
846 
847     TRACE("NtUserOpenWindowStation called\n");
848 
849     /* Capture the object attributes and the window station name */
850     _SEH2_TRY
851     {
852         ProbeForRead(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG));
853         LocalObjectAttributes = *ObjectAttributes;
854         if (LocalObjectAttributes.Length != sizeof(OBJECT_ATTRIBUTES))
855         {
856             ERR("Invalid ObjectAttributes length!\n");
857             Status = STATUS_INVALID_PARAMETER;
858             _SEH2_LEAVE;
859         }
860 
861         /*
862          * Check whether the caller did not provide a window station name,
863          * or provided the special "Service-0x00000000-00000000$" name.
864          *
865          * NOTE: On Windows, the special "Service-0x00000000-00000000$" string
866          * is used instead of an empty name (observed when API-monitoring
867          * OpenWindowStation() called with an empty window station name).
868          */
869         if ((LocalObjectAttributes.ObjectName == NULL ||
870              LocalObjectAttributes.ObjectName->Buffer == NULL ||
871              LocalObjectAttributes.ObjectName->Length == 0 ||
872              LocalObjectAttributes.ObjectName->Buffer[0] == UNICODE_NULL)
873             /* &&
874             LocalObjectAttributes.RootDirectory == NULL */)
875         {
876             /* No, remember that for later */
877             LocalObjectAttributes.ObjectName = NULL;
878         }
879         if (LocalObjectAttributes.ObjectName &&
880             LocalObjectAttributes.ObjectName->Length ==
881                 sizeof(L"Service-0x00000000-00000000$") - sizeof(UNICODE_NULL) &&
882             _wcsnicmp(LocalObjectAttributes.ObjectName->Buffer,
883                       L"Service-0x00000000-00000000$",
884                       LocalObjectAttributes.ObjectName->Length / sizeof(WCHAR)) == 0)
885         {
886             /* No, remember that for later */
887             LocalObjectAttributes.ObjectName = NULL;
888         }
889 
890         /*
891          * If the caller did not provide a window station name, build a new one
892          * based on the logon session identifier for the calling process.
893          * The new name is allocated in user-mode, as the rest of ObjectAttributes
894          * already is, so that the validation performed by the Object Manager
895          * can be done adequately.
896          */
897         if (!LocalObjectAttributes.ObjectName)
898         {
899             /* No, build the new window station name */
900             Status = BuildUserModeWindowStationName(ObjectAttributes,
901                                                     &LocalObjectAttributes,
902                                                     &WindowStationName,
903                                                     &TebStaticUnicodeString);
904             if (!NT_SUCCESS(Status))
905             {
906                 ERR("BuildUserModeWindowStationName() failed, Status 0x%08lx\n", Status);
907                 _SEH2_LEAVE;
908             }
909             OwnerMode = KernelMode;
910         }
911     }
912     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
913     {
914         Status =_SEH2_GetExceptionCode();
915         ERR("ObjectAttributes capture failed, Status 0x%08lx\n", Status);
916     }
917     _SEH2_END;
918 
919     if (!NT_SUCCESS(Status))
920     {
921         SetLastNtError(Status);
922         return NULL;
923     }
924 
925     /* Open the window station */
926     Status = ObOpenObjectByName(ObjectAttributes,
927                                 ExWindowStationObjectType,
928                                 UserMode,
929                                 NULL,
930                                 dwDesiredAccess,
931                                 NULL,
932                                 (PVOID*)&hWinSta);
933     if (NT_SUCCESS(Status))
934     {
935         TRACE("NtUserOpenWindowStation opened window station '%wZ' with handle 0x%p\n",
936               ObjectAttributes->ObjectName, hWinSta);
937     }
938     else
939     {
940         ASSERT(hWinSta == NULL);
941         ERR("NtUserOpenWindowStation failed to open window station '%wZ', Status 0x%08lx\n",
942             ObjectAttributes->ObjectName, Status);
943     }
944 
945     /* Try to restore the user's ObjectAttributes and release the window station name */
946     FreeUserModeWindowStationName(WindowStationName,
947                                   TebStaticUnicodeString,
948                                   (OwnerMode == KernelMode ? ObjectAttributes : NULL),
949                                   &LocalObjectAttributes);
950 
951     if (!NT_SUCCESS(Status))
952     {
953         ASSERT(hWinSta == NULL);
954         SetLastNtError(Status);
955     }
956 
957     return hWinSta;
958 }
959 
960 /*
961  * NtUserCloseWindowStation
962  *
963  * Closes a window station handle.
964  *
965  * Parameters
966  *    hWinSta
967  *       Handle to the window station.
968  *
969  * Return Value
970  *    Status
971  *
972  * Remarks
973  *    The window station handle can be created with NtUserCreateWindowStation
974  *    or NtUserOpenWindowStation. Attempts to close a handle to the window
975  *    station assigned to the calling process will fail.
976  *
977  * Status
978  *    @implemented
979  */
980 
981 BOOL
982 APIENTRY
983 NtUserCloseWindowStation(
984     HWINSTA hWinSta)
985 {
986     PWINSTATION_OBJECT Object;
987     NTSTATUS Status;
988 
989     TRACE("NtUserCloseWindowStation called (%p)\n", hWinSta);
990 
991     if (hWinSta == UserGetProcessWindowStation())
992     {
993         ERR("Attempted to close process window station\n");
994         return FALSE;
995     }
996 
997     Status = IntValidateWindowStationHandle(hWinSta,
998                                             UserMode,
999                                             0,
1000                                             &Object,
1001                                             NULL);
1002     if (!NT_SUCCESS(Status))
1003     {
1004         ERR("Validation of window station handle (%p) failed\n", hWinSta);
1005         return FALSE;
1006     }
1007 
1008     ObDereferenceObject(Object);
1009 
1010     TRACE("Closing window station handle (%p)\n", hWinSta);
1011 
1012     Status = ObCloseHandle(hWinSta, UserMode);
1013     if (!NT_SUCCESS(Status))
1014     {
1015         SetLastNtError(Status);
1016         return FALSE;
1017     }
1018 
1019     return TRUE;
1020 }
1021 
1022 /*
1023  * NtUserGetObjectInformation
1024  *
1025  * The NtUserGetObjectInformation function retrieves information about a
1026  * window station or desktop object.
1027  *
1028  * Parameters
1029  *    hObj
1030  *       Handle to the window station or desktop object for which to
1031  *       return information. This can be a handle of type HDESK or HWINSTA
1032  *       (for example, a handle returned by NtUserCreateWindowStation,
1033  *       NtUserOpenWindowStation, NtUserCreateDesktop, or NtUserOpenDesktop).
1034  *
1035  *    nIndex
1036  *       Specifies the object information to be retrieved.
1037  *
1038  *    pvInfo
1039  *       Pointer to a buffer to receive the object information.
1040  *
1041  *    nLength
1042  *       Specifies the size, in bytes, of the buffer pointed to by the
1043  *       pvInfo parameter.
1044  *
1045  *    lpnLengthNeeded
1046  *       Pointer to a variable receiving the number of bytes required to
1047  *       store the requested information. If this variable's value is
1048  *       greater than the value of the nLength parameter when the function
1049  *       returns, the function returns FALSE, and none of the information
1050  *       is copied to the pvInfo buffer. If the value of the variable pointed
1051  *       to by lpnLengthNeeded is less than or equal to the value of nLength,
1052  *       the entire information block is copied.
1053  *
1054  * Return Value
1055  *    If the function succeeds, the return value is nonzero. If the function
1056  *    fails, the return value is zero.
1057  *
1058  * Status
1059  *    @unimplemented
1060  */
1061 
1062 BOOL APIENTRY
1063 NtUserGetObjectInformation(
1064     HANDLE hObject,
1065     DWORD nIndex,
1066     PVOID pvInformation,
1067     DWORD nLength,
1068     PDWORD nLengthNeeded)
1069 {
1070     NTSTATUS Status;
1071     PWINSTATION_OBJECT WinStaObject = NULL;
1072     PDESKTOP DesktopObject = NULL;
1073     POBJECT_HEADER ObjectHeader;
1074     POBJECT_HEADER_NAME_INFO NameInfo;
1075     OBJECT_HANDLE_INFORMATION HandleInfo;
1076     USEROBJECTFLAGS ObjectFlags;
1077     PUNICODE_STRING pStrNameU = NULL;
1078     PVOID pvData = NULL;
1079     SIZE_T nDataSize = 0;
1080 
1081     _SEH2_TRY
1082     {
1083         if (nLengthNeeded)
1084             ProbeForWrite(nLengthNeeded, sizeof(*nLengthNeeded), 1);
1085         ProbeForWrite(pvInformation, nLength, 1);
1086     }
1087     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1088     {
1089         SetLastNtError(_SEH2_GetExceptionCode());
1090         return FALSE;
1091     }
1092     _SEH2_END;
1093 
1094     /* Try window station */
1095     TRACE("Trying to open window station 0x%p\n", hObject);
1096     Status = ObReferenceObjectByHandle(hObject,
1097                                        0,
1098                                        ExWindowStationObjectType,
1099                                        UserMode,
1100                                        (PVOID*)&WinStaObject,
1101                                        &HandleInfo);
1102 
1103     if (Status == STATUS_OBJECT_TYPE_MISMATCH)
1104     {
1105         /* Try desktop */
1106         TRACE("Trying to open desktop %p\n", hObject);
1107         WinStaObject = NULL;
1108         Status = IntValidateDesktopHandle(hObject,
1109                                           UserMode,
1110                                           0,
1111                                           &DesktopObject);
1112     }
1113 
1114     if (!NT_SUCCESS(Status))
1115     {
1116         ERR("Failed: 0x%x\n", Status);
1117         goto Exit;
1118     }
1119 
1120     TRACE("WinSta or Desktop opened!\n");
1121 
1122     /* Get data */
1123     switch (nIndex)
1124     {
1125         case UOI_FLAGS:
1126         {
1127             ObjectFlags.fReserved = FALSE;
1128             ObjectFlags.fInherit = !!(HandleInfo.HandleAttributes & OBJ_INHERIT);
1129 
1130             ObjectFlags.dwFlags = 0;
1131             if (WinStaObject != NULL)
1132             {
1133                 if (!(WinStaObject->Flags & WSS_NOIO))
1134                     ObjectFlags.dwFlags |= WSF_VISIBLE;
1135             }
1136             else if (DesktopObject != NULL)
1137             {
1138                 FIXME("Setting DF_ALLOWOTHERACCOUNTHOOK is unimplemented.\n");
1139             }
1140             else
1141             {
1142                 ERR("No associated WinStaObject nor DesktopObject!\n");
1143             }
1144 
1145             pvData = &ObjectFlags;
1146             nDataSize = sizeof(ObjectFlags);
1147             Status = STATUS_SUCCESS;
1148             break;
1149         }
1150 
1151         case UOI_NAME:
1152         {
1153             if (WinStaObject != NULL)
1154             {
1155                 ObjectHeader = OBJECT_TO_OBJECT_HEADER(WinStaObject);
1156                 NameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
1157 
1158                 if (NameInfo && (NameInfo->Name.Length > 0))
1159                 {
1160                     /* Named window station */
1161                     pStrNameU = &NameInfo->Name;
1162                     nDataSize = pStrNameU->Length + sizeof(UNICODE_NULL);
1163                 }
1164                 else
1165                 {
1166                     /* Unnamed window station (should never happen!) */
1167                     ASSERT(FALSE);
1168                     pStrNameU = NULL;
1169                     nDataSize = sizeof(UNICODE_NULL);
1170                 }
1171                 Status = STATUS_SUCCESS;
1172             }
1173             else if (DesktopObject != NULL)
1174             {
1175                 pvData = DesktopObject->pDeskInfo->szDesktopName;
1176                 nDataSize = (wcslen(DesktopObject->pDeskInfo->szDesktopName) + 1) * sizeof(WCHAR);
1177                 Status = STATUS_SUCCESS;
1178             }
1179             else
1180             {
1181                 Status = STATUS_INVALID_PARAMETER;
1182             }
1183             break;
1184         }
1185 
1186         case UOI_TYPE:
1187         {
1188             if (WinStaObject != NULL)
1189             {
1190                 ObjectHeader = OBJECT_TO_OBJECT_HEADER(WinStaObject);
1191                 pStrNameU = &ObjectHeader->Type->Name;
1192                 nDataSize = pStrNameU->Length + sizeof(UNICODE_NULL);
1193                 Status = STATUS_SUCCESS;
1194             }
1195             else if (DesktopObject != NULL)
1196             {
1197                 ObjectHeader = OBJECT_TO_OBJECT_HEADER(DesktopObject);
1198                 pStrNameU = &ObjectHeader->Type->Name;
1199                 nDataSize = pStrNameU->Length + sizeof(UNICODE_NULL);
1200                 Status = STATUS_SUCCESS;
1201             }
1202             else
1203             {
1204                 Status = STATUS_INVALID_PARAMETER;
1205             }
1206             break;
1207         }
1208 
1209         case UOI_USER_SID:
1210             Status = STATUS_NOT_IMPLEMENTED;
1211             ERR("UOI_USER_SID unimplemented!\n");
1212             break;
1213 
1214         default:
1215             Status = STATUS_INVALID_PARAMETER;
1216             break;
1217     }
1218 
1219 Exit:
1220     if ((Status == STATUS_SUCCESS) && (nLength < nDataSize))
1221         Status = STATUS_BUFFER_TOO_SMALL;
1222 
1223     _SEH2_TRY
1224     {
1225         if (nLengthNeeded)
1226             *nLengthNeeded = nDataSize;
1227 
1228         /* Try to copy data to caller */
1229         if (Status == STATUS_SUCCESS && (nDataSize > 0))
1230         {
1231             TRACE("Trying to copy data to caller (len = %lu, len needed = %lu)\n", nLength, nDataSize);
1232             if (pvData)
1233             {
1234                 /* Copy the data */
1235                 RtlCopyMemory(pvInformation, pvData, nDataSize);
1236             }
1237             else if (pStrNameU)
1238             {
1239                 /* Copy and NULL-terminate the string */
1240                 RtlCopyMemory(pvInformation, pStrNameU->Buffer, pStrNameU->Length);
1241                 ((PWCHAR)pvInformation)[pStrNameU->Length / sizeof(WCHAR)] = UNICODE_NULL;
1242             }
1243             else
1244             {
1245                 /* Zero the memory */
1246                 RtlZeroMemory(pvInformation, nDataSize);
1247             }
1248         }
1249     }
1250     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1251     {
1252         Status = _SEH2_GetExceptionCode();
1253     }
1254     _SEH2_END;
1255 
1256     /* Release objects */
1257     if (DesktopObject != NULL)
1258         ObDereferenceObject(DesktopObject);
1259     if (WinStaObject != NULL)
1260         ObDereferenceObject(WinStaObject);
1261 
1262     if (!NT_SUCCESS(Status))
1263     {
1264         SetLastNtError(Status);
1265         return FALSE;
1266     }
1267 
1268     return TRUE;
1269 }
1270 
1271 /*
1272  * NtUserSetObjectInformation
1273  *
1274  * The NtUserSetObjectInformation function sets information about a
1275  * window station or desktop object.
1276  *
1277  * Parameters
1278  *    hObj
1279  *       Handle to the window station or desktop object for which to set
1280  *       object information. This value can be a handle of type HDESK or
1281  *       HWINSTA.
1282  *
1283  *    nIndex
1284  *       Specifies the object information to be set.
1285  *
1286  *    pvInfo
1287  *       Pointer to a buffer containing the object information.
1288  *
1289  *    nLength
1290  *       Specifies the size, in bytes, of the information contained in the
1291  *       buffer pointed to by pvInfo.
1292  *
1293  * Return Value
1294  *    If the function succeeds, the return value is nonzero. If the function
1295  *    fails the return value is zero.
1296  *
1297  * Status
1298  *    @unimplemented
1299  */
1300 
1301 BOOL
1302 APIENTRY
1303 NtUserSetObjectInformation(
1304     HANDLE hObject,
1305     DWORD nIndex,
1306     PVOID pvInformation,
1307     DWORD nLength)
1308 {
1309     /* FIXME: ZwQueryObject */
1310     /* FIXME: ZwSetInformationObject */
1311     SetLastNtError(STATUS_UNSUCCESSFUL);
1312     return FALSE;
1313 }
1314 
1315 
1316 HWINSTA FASTCALL
1317 UserGetProcessWindowStation(VOID)
1318 {
1319     PPROCESSINFO ppi = PsGetCurrentProcessWin32Process();
1320 
1321     return ppi->hwinsta;
1322 }
1323 
1324 
1325 /*
1326  * NtUserGetProcessWindowStation
1327  *
1328  * Returns a handle to the current process window station.
1329  *
1330  * Return Value
1331  *    If the function succeeds, the return value is handle to the window
1332  *    station assigned to the current process. If the function fails, the
1333  *    return value is NULL.
1334  *
1335  * Status
1336  *    @implemented
1337  */
1338 
1339 HWINSTA APIENTRY
1340 NtUserGetProcessWindowStation(VOID)
1341 {
1342     return UserGetProcessWindowStation();
1343 }
1344 
1345 BOOL FASTCALL
1346 UserSetProcessWindowStation(HWINSTA hWindowStation)
1347 {
1348     NTSTATUS Status;
1349     PPROCESSINFO ppi;
1350     OBJECT_HANDLE_INFORMATION ObjectHandleInfo;
1351     PWINSTATION_OBJECT NewWinSta = NULL, OldWinSta;
1352     HWINSTA hCacheWinSta;
1353 
1354     ppi = PsGetCurrentProcessWin32Process();
1355 
1356     /* Reference the new window station */
1357     if (hWindowStation != NULL)
1358     {
1359         Status = IntValidateWindowStationHandle(hWindowStation,
1360                                                 UserMode,
1361                                                 0,
1362                                                 &NewWinSta,
1363                                                 &ObjectHandleInfo);
1364         if (!NT_SUCCESS(Status))
1365         {
1366             TRACE("Validation of window station handle 0x%p failed\n", hWindowStation);
1367             SetLastNtError(Status);
1368             return FALSE;
1369         }
1370     }
1371 
1372     OldWinSta = ppi->prpwinsta;
1373     hCacheWinSta = PsGetProcessWin32WindowStation(ppi->peProcess);
1374 
1375     /* Dereference the previous window station */
1376     if (OldWinSta != NULL)
1377     {
1378         ObDereferenceObject(OldWinSta);
1379     }
1380 
1381     /*
1382      * FIXME: Don't allow changing the window station if there are threads that are attached to desktops and own GUI objects?
1383      */
1384 
1385     /* Close the cached EPROCESS window station handle if needed */
1386     if (hCacheWinSta != NULL)
1387     {
1388         /* Reference the window station */
1389         Status = ObReferenceObjectByHandle(hCacheWinSta,
1390                                            0,
1391                                            ExWindowStationObjectType,
1392                                            UserMode,
1393                                            (PVOID*)&OldWinSta,
1394                                            NULL);
1395         if (!NT_SUCCESS(Status))
1396         {
1397             ERR("Failed to reference the inherited window station, Status 0x%08lx\n", Status);
1398             /* We failed, reset the cache */
1399             hCacheWinSta = NULL;
1400             PsSetProcessWindowStation(ppi->peProcess, hCacheWinSta);
1401         }
1402         else
1403         {
1404             /*
1405              * Close the old handle and reset the cache only
1406              * if we are setting a different window station.
1407              */
1408             if (NewWinSta != OldWinSta)
1409             {
1410                 ObCloseHandle(hCacheWinSta, UserMode);
1411                 hCacheWinSta = NULL;
1412                 PsSetProcessWindowStation(ppi->peProcess, hCacheWinSta);
1413             }
1414 
1415             /* Dereference the window station */
1416             ObDereferenceObject(OldWinSta);
1417         }
1418     }
1419 
1420     /* Duplicate and save a new cached EPROCESS window station handle */
1421     if ((hCacheWinSta == NULL) && (hWindowStation != NULL))
1422     {
1423         Status = ZwDuplicateObject(ZwCurrentProcess(),
1424                                    hWindowStation,
1425                                    ZwCurrentProcess(),
1426                                    (PHANDLE)&hCacheWinSta,
1427                                    0,
1428                                    0,
1429                                    DUPLICATE_SAME_ACCESS);
1430         if (!NT_SUCCESS(Status))
1431         {
1432             ERR("UserSetProcessWindowStation: Failed to duplicate the window station handle, Status 0x%08lx\n", Status);
1433         }
1434         else
1435         {
1436             PsSetProcessWindowStation(ppi->peProcess, hCacheWinSta);
1437         }
1438     }
1439 
1440     ppi->prpwinsta = NewWinSta;
1441     ppi->hwinsta = hWindowStation;
1442     ppi->amwinsta = hWindowStation != NULL ? ObjectHandleInfo.GrantedAccess : 0;
1443     TRACE("WS : Granted Access 0x%08lx\n",ppi->amwinsta);
1444 
1445     if (RtlAreAllAccessesGranted(ppi->amwinsta, WINSTA_READSCREEN))
1446     {
1447         ppi->W32PF_flags |= W32PF_READSCREENACCESSGRANTED;
1448     }
1449     else
1450     {
1451         ppi->W32PF_flags &= ~W32PF_READSCREENACCESSGRANTED;
1452     }
1453 
1454     if (NewWinSta && !(NewWinSta->Flags & WSS_NOIO))
1455     {
1456         ppi->W32PF_flags |= W32PF_IOWINSTA;
1457     }
1458     else /* Might be closed if the handle is NULL */
1459     {
1460         ppi->W32PF_flags &= ~W32PF_IOWINSTA;
1461     }
1462     return TRUE;
1463 }
1464 
1465 /*
1466  * NtUserSetProcessWindowStation
1467  *
1468  * Assigns a window station to the current process.
1469  *
1470  * Parameters
1471  *    hWinSta
1472  *       Handle to the window station.
1473  *
1474  * Return Value
1475  *    Status
1476  *
1477  * Status
1478  *    @implemented
1479  */
1480 
1481 BOOL APIENTRY
1482 NtUserSetProcessWindowStation(HWINSTA hWindowStation)
1483 {
1484     BOOL ret;
1485 
1486     UserEnterExclusive();
1487 
1488     ret = UserSetProcessWindowStation(hWindowStation);
1489 
1490     UserLeave();
1491 
1492     return ret;
1493 }
1494 
1495 /*
1496  * NtUserLockWindowStation
1497  *
1498  * Locks switching desktops. Only the logon application is allowed to call this function.
1499  *
1500  * Status
1501  *    @implemented
1502  */
1503 
1504 BOOL APIENTRY
1505 NtUserLockWindowStation(HWINSTA hWindowStation)
1506 {
1507     PWINSTATION_OBJECT Object;
1508     NTSTATUS Status;
1509 
1510     TRACE("About to set process window station with handle (%p)\n",
1511           hWindowStation);
1512 
1513     if (gpidLogon != PsGetCurrentProcessId())
1514     {
1515         ERR("Unauthorized process attempted to lock the window station!\n");
1516         EngSetLastError(ERROR_ACCESS_DENIED);
1517         return FALSE;
1518     }
1519 
1520     Status = IntValidateWindowStationHandle(hWindowStation,
1521                                             UserMode,
1522                                             0,
1523                                             &Object,
1524                                             NULL);
1525     if (!NT_SUCCESS(Status))
1526     {
1527         TRACE("Validation of window station handle (%p) failed\n",
1528               hWindowStation);
1529         SetLastNtError(Status);
1530         return FALSE;
1531     }
1532 
1533     Object->Flags |= WSS_LOCKED;
1534 
1535     ObDereferenceObject(Object);
1536     return TRUE;
1537 }
1538 
1539 /*
1540  * NtUserUnlockWindowStation
1541  *
1542  * Unlocks switching desktops. Only the logon application is allowed to call this function.
1543  *
1544  * Status
1545  *    @implemented
1546  */
1547 
1548 BOOL APIENTRY
1549 NtUserUnlockWindowStation(HWINSTA hWindowStation)
1550 {
1551     PWINSTATION_OBJECT Object;
1552     NTSTATUS Status;
1553     BOOL Ret;
1554 
1555     TRACE("About to set process window station with handle (%p)\n",
1556           hWindowStation);
1557 
1558     if (gpidLogon != PsGetCurrentProcessId())
1559     {
1560         ERR("Unauthorized process attempted to unlock the window station!\n");
1561         EngSetLastError(ERROR_ACCESS_DENIED);
1562         return FALSE;
1563     }
1564 
1565     Status = IntValidateWindowStationHandle(hWindowStation,
1566                                             UserMode,
1567                                             0,
1568                                             &Object,
1569                                             NULL);
1570     if (!NT_SUCCESS(Status))
1571     {
1572         TRACE("Validation of window station handle (%p) failed\n",
1573               hWindowStation);
1574         SetLastNtError(Status);
1575         return FALSE;
1576     }
1577 
1578     Ret = (Object->Flags & WSS_LOCKED) == WSS_LOCKED;
1579     Object->Flags &= ~WSS_LOCKED;
1580 
1581     ObDereferenceObject(Object);
1582     return Ret;
1583 }
1584 
1585 static NTSTATUS FASTCALL
1586 BuildWindowStationNameList(
1587     ULONG dwSize,
1588     PVOID lpBuffer,
1589     PULONG pRequiredSize)
1590 {
1591     OBJECT_ATTRIBUTES ObjectAttributes;
1592     NTSTATUS Status;
1593     HANDLE DirectoryHandle;
1594     char InitialBuffer[256], *Buffer;
1595     ULONG Context, ReturnLength, BufferSize;
1596     DWORD EntryCount;
1597     POBJECT_DIRECTORY_INFORMATION DirEntry;
1598     WCHAR NullWchar;
1599 
1600     //
1601     // FIXME: Fully wrong! Since, by calling NtUserCreateWindowStation
1602     // with judicious parameters one can create window stations elsewhere
1603     // than in Windows\WindowStations directory, Win32k definitely MUST
1604     // maintain a list of window stations it has created, and not rely
1605     // on the enumeration of Windows\WindowStations !!!
1606     //
1607 
1608     /*
1609      * Try to open the directory.
1610      */
1611     InitializeObjectAttributes(&ObjectAttributes,
1612                                &gustrWindowStationsDir,
1613                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1614                                NULL,
1615                                NULL);
1616 
1617     Status = ZwOpenDirectoryObject(&DirectoryHandle,
1618                                    DIRECTORY_QUERY,
1619                                    &ObjectAttributes);
1620 
1621     if (!NT_SUCCESS(Status))
1622     {
1623         return Status;
1624     }
1625 
1626     /* First try to query the directory using a fixed-size buffer */
1627     Context = 0;
1628     Buffer = NULL;
1629     Status = ZwQueryDirectoryObject(DirectoryHandle,
1630                                     InitialBuffer,
1631                                     sizeof(InitialBuffer),
1632                                     FALSE,
1633                                     TRUE,
1634                                     &Context,
1635                                     &ReturnLength);
1636     if (NT_SUCCESS(Status))
1637     {
1638         if (STATUS_NO_MORE_ENTRIES == ZwQueryDirectoryObject(DirectoryHandle, NULL, 0, FALSE,
1639                                                              FALSE, &Context, NULL))
1640         {
1641             /* Our fixed-size buffer is large enough */
1642             Buffer = InitialBuffer;
1643         }
1644     }
1645 
1646     if (NULL == Buffer)
1647     {
1648         /* Need a larger buffer, check how large exactly */
1649         Status = ZwQueryDirectoryObject(DirectoryHandle, NULL, 0, FALSE, TRUE, &Context,
1650                                         &ReturnLength);
1651         if (!NT_SUCCESS(Status))
1652         {
1653             ERR("ZwQueryDirectoryObject failed\n");
1654             ZwClose(DirectoryHandle);
1655             return Status;
1656         }
1657 
1658         BufferSize = ReturnLength;
1659         Buffer = ExAllocatePoolWithTag(PagedPool, BufferSize, TAG_WINSTA);
1660         if (NULL == Buffer)
1661         {
1662             ZwClose(DirectoryHandle);
1663             return STATUS_NO_MEMORY;
1664         }
1665 
1666         /* We should have a sufficiently large buffer now */
1667         Context = 0;
1668         Status = ZwQueryDirectoryObject(DirectoryHandle, Buffer, BufferSize,
1669                                         FALSE, TRUE, &Context, &ReturnLength);
1670         if (! NT_SUCCESS(Status) ||
1671               STATUS_NO_MORE_ENTRIES != ZwQueryDirectoryObject(DirectoryHandle, NULL, 0, FALSE,
1672                                                                FALSE, &Context, NULL))
1673         {
1674             /* Something went wrong, maybe someone added a directory entry? Just give up. */
1675             ExFreePoolWithTag(Buffer, TAG_WINSTA);
1676             ZwClose(DirectoryHandle);
1677             return NT_SUCCESS(Status) ? STATUS_INTERNAL_ERROR : Status;
1678         }
1679     }
1680 
1681     ZwClose(DirectoryHandle);
1682 
1683     /*
1684      * Count the required size of buffer.
1685      */
1686     ReturnLength = sizeof(DWORD);
1687     EntryCount = 0;
1688     for (DirEntry = (POBJECT_DIRECTORY_INFORMATION) Buffer;
1689          0 != DirEntry->Name.Length;
1690          DirEntry++)
1691     {
1692         ReturnLength += DirEntry->Name.Length + sizeof(WCHAR);
1693         EntryCount++;
1694     }
1695     TRACE("Required size: %lu Entry count: %lu\n", ReturnLength, EntryCount);
1696     if (NULL != pRequiredSize)
1697     {
1698         Status = MmCopyToCaller(pRequiredSize, &ReturnLength, sizeof(ULONG));
1699         if (! NT_SUCCESS(Status))
1700         {
1701             if (Buffer != InitialBuffer)
1702             {
1703                 ExFreePoolWithTag(Buffer, TAG_WINSTA);
1704             }
1705             return STATUS_BUFFER_TOO_SMALL;
1706         }
1707     }
1708 
1709     /*
1710      * Check if the supplied buffer is large enough.
1711      */
1712     if (dwSize < ReturnLength)
1713     {
1714         if (Buffer != InitialBuffer)
1715         {
1716             ExFreePoolWithTag(Buffer, TAG_WINSTA);
1717         }
1718         return STATUS_BUFFER_TOO_SMALL;
1719     }
1720 
1721     /*
1722      * Generate the resulting buffer contents.
1723      */
1724     Status = MmCopyToCaller(lpBuffer, &EntryCount, sizeof(DWORD));
1725     if (! NT_SUCCESS(Status))
1726     {
1727         if (Buffer != InitialBuffer)
1728         {
1729             ExFreePoolWithTag(Buffer, TAG_WINSTA);
1730         }
1731         return Status;
1732     }
1733     lpBuffer = (PVOID) ((PCHAR) lpBuffer + sizeof(DWORD));
1734 
1735     NullWchar = L'\0';
1736     for (DirEntry = (POBJECT_DIRECTORY_INFORMATION) Buffer;
1737          0 != DirEntry->Name.Length;
1738          DirEntry++)
1739     {
1740         Status = MmCopyToCaller(lpBuffer, DirEntry->Name.Buffer, DirEntry->Name.Length);
1741         if (! NT_SUCCESS(Status))
1742         {
1743             if (Buffer != InitialBuffer)
1744             {
1745                 ExFreePoolWithTag(Buffer, TAG_WINSTA);
1746             }
1747             return Status;
1748         }
1749         lpBuffer = (PVOID) ((PCHAR) lpBuffer + DirEntry->Name.Length);
1750         Status = MmCopyToCaller(lpBuffer, &NullWchar, sizeof(WCHAR));
1751         if (! NT_SUCCESS(Status))
1752         {
1753             if (Buffer != InitialBuffer)
1754             {
1755                 ExFreePoolWithTag(Buffer, TAG_WINSTA);
1756             }
1757             return Status;
1758         }
1759         lpBuffer = (PVOID) ((PCHAR) lpBuffer + sizeof(WCHAR));
1760     }
1761 
1762     /*
1763      * Clean up
1764      */
1765     if (Buffer != InitialBuffer)
1766     {
1767         ExFreePoolWithTag(Buffer, TAG_WINSTA);
1768     }
1769 
1770     return STATUS_SUCCESS;
1771 }
1772 
1773 static NTSTATUS FASTCALL
1774 BuildDesktopNameList(
1775     HWINSTA hWindowStation,
1776     ULONG dwSize,
1777     PVOID lpBuffer,
1778     PULONG pRequiredSize)
1779 {
1780     NTSTATUS Status;
1781     PWINSTATION_OBJECT WindowStation;
1782     PLIST_ENTRY DesktopEntry;
1783     PDESKTOP DesktopObject;
1784     DWORD EntryCount;
1785     ULONG ReturnLength;
1786     WCHAR NullWchar;
1787     UNICODE_STRING DesktopName;
1788 
1789     Status = IntValidateWindowStationHandle(hWindowStation,
1790                                             UserMode,
1791                                             0,
1792                                             &WindowStation,
1793                                             NULL);
1794     if (! NT_SUCCESS(Status))
1795     {
1796         return Status;
1797     }
1798 
1799     /*
1800      * Count the required size of buffer.
1801      */
1802     ReturnLength = sizeof(DWORD);
1803     EntryCount = 0;
1804     for (DesktopEntry = WindowStation->DesktopListHead.Flink;
1805          DesktopEntry != &WindowStation->DesktopListHead;
1806          DesktopEntry = DesktopEntry->Flink)
1807     {
1808         DesktopObject = CONTAINING_RECORD(DesktopEntry, DESKTOP, ListEntry);
1809         RtlInitUnicodeString(&DesktopName, DesktopObject->pDeskInfo->szDesktopName);
1810         ReturnLength += DesktopName.Length + sizeof(WCHAR);
1811         EntryCount++;
1812     }
1813     TRACE("Required size: %lu Entry count: %lu\n", ReturnLength, EntryCount);
1814     if (NULL != pRequiredSize)
1815     {
1816         Status = MmCopyToCaller(pRequiredSize, &ReturnLength, sizeof(ULONG));
1817         if (! NT_SUCCESS(Status))
1818         {
1819             ObDereferenceObject(WindowStation);
1820             return STATUS_BUFFER_TOO_SMALL;
1821         }
1822     }
1823 
1824     /*
1825      * Check if the supplied buffer is large enough.
1826      */
1827     if (dwSize < ReturnLength)
1828     {
1829         ObDereferenceObject(WindowStation);
1830         return STATUS_BUFFER_TOO_SMALL;
1831     }
1832 
1833     /*
1834      * Generate the resulting buffer contents.
1835      */
1836     Status = MmCopyToCaller(lpBuffer, &EntryCount, sizeof(DWORD));
1837     if (! NT_SUCCESS(Status))
1838     {
1839         ObDereferenceObject(WindowStation);
1840         return Status;
1841     }
1842     lpBuffer = (PVOID) ((PCHAR) lpBuffer + sizeof(DWORD));
1843 
1844     NullWchar = L'\0';
1845     for (DesktopEntry = WindowStation->DesktopListHead.Flink;
1846          DesktopEntry != &WindowStation->DesktopListHead;
1847          DesktopEntry = DesktopEntry->Flink)
1848     {
1849         DesktopObject = CONTAINING_RECORD(DesktopEntry, DESKTOP, ListEntry);
1850         RtlInitUnicodeString(&DesktopName, DesktopObject->pDeskInfo->szDesktopName);
1851         Status = MmCopyToCaller(lpBuffer, DesktopName.Buffer, DesktopName.Length);
1852         if (! NT_SUCCESS(Status))
1853         {
1854             ObDereferenceObject(WindowStation);
1855             return Status;
1856         }
1857         lpBuffer = (PVOID) ((PCHAR)lpBuffer + DesktopName.Length);
1858         Status = MmCopyToCaller(lpBuffer, &NullWchar, sizeof(WCHAR));
1859         if (! NT_SUCCESS(Status))
1860         {
1861             ObDereferenceObject(WindowStation);
1862             return Status;
1863         }
1864         lpBuffer = (PVOID) ((PCHAR) lpBuffer + sizeof(WCHAR));
1865     }
1866 
1867     /*
1868      * Clean up and return
1869      */
1870     ObDereferenceObject(WindowStation);
1871     return STATUS_SUCCESS;
1872 }
1873 
1874 /*
1875  * NtUserBuildNameList
1876  *
1877  * Function used for enumeration of desktops or window stations.
1878  *
1879  * Parameters
1880  *    hWinSta
1881  *       For enumeration of window stations this parameter must be set to
1882  *       zero. Otherwise it's handle for window station.
1883  *
1884  *    dwSize
1885  *       Size of buffer passed by caller.
1886  *
1887  *    lpBuffer
1888  *       Buffer passed by caller. If the function succeeds, the buffer is
1889  *       filled with window station/desktop count (in first DWORD) and
1890  *       NULL-terminated window station/desktop names.
1891  *
1892  *    pRequiredSize
1893  *       If the function succeeds, this is the number of bytes copied.
1894  *       Otherwise it's size of buffer needed for function to succeed.
1895  *
1896  * Status
1897  *    @implemented
1898  */
1899 
1900 NTSTATUS APIENTRY
1901 NtUserBuildNameList(
1902     HWINSTA hWindowStation,
1903     ULONG dwSize,
1904     PVOID lpBuffer,
1905     PULONG pRequiredSize)
1906 {
1907     /* The WindowStation name list and desktop name list are build in completely
1908        different ways. Call the appropriate function */
1909     return NULL == hWindowStation ? BuildWindowStationNameList(dwSize, lpBuffer, pRequiredSize) :
1910            BuildDesktopNameList(hWindowStation, dwSize, lpBuffer, pRequiredSize);
1911 }
1912 
1913 /*
1914  * @implemented
1915  */
1916 BOOL APIENTRY
1917 NtUserSetLogonNotifyWindow(HWND hWnd)
1918 {
1919     if (gpidLogon != PsGetCurrentProcessId())
1920     {
1921         return FALSE;
1922     }
1923 
1924     if (!IntIsWindow(hWnd))
1925     {
1926         return FALSE;
1927     }
1928 
1929     hwndSAS = hWnd;
1930 
1931     return TRUE;
1932 }
1933 
1934 BOOL
1935 APIENTRY
1936 NtUserLockWorkStation(VOID)
1937 {
1938     BOOL ret;
1939     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1940 
1941     UserEnterExclusive();
1942 
1943     if (pti->rpdesk == IntGetActiveDesktop())
1944     {
1945         ret = UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_LOCK_WORKSTATION, 0);
1946     }
1947     else
1948     {
1949         ret = FALSE;
1950     }
1951 
1952     UserLeave();
1953 
1954     return ret;
1955 }
1956 
1957 BOOL
1958 NTAPI
1959 NtUserSetWindowStationUser(
1960     IN HWINSTA hWindowStation,
1961     IN PLUID pluid,
1962     IN PSID psid OPTIONAL,
1963     IN DWORD size)
1964 {
1965     BOOL Ret = FALSE;
1966     NTSTATUS Status;
1967     PWINSTATION_OBJECT WindowStation = NULL;
1968     LUID luidUser;
1969 
1970     UserEnterExclusive();
1971 
1972     if (gpidLogon != PsGetCurrentProcessId())
1973     {
1974         EngSetLastError(ERROR_ACCESS_DENIED);
1975         goto Leave;
1976     }
1977 
1978     /* Validate the window station */
1979     Status = IntValidateWindowStationHandle(hWindowStation,
1980                                             UserMode,
1981                                             0,
1982                                             &WindowStation,
1983                                             NULL);
1984     if (!NT_SUCCESS(Status))
1985     {
1986         goto Leave;
1987     }
1988 
1989     /* Capture the user LUID */
1990     _SEH2_TRY
1991     {
1992         ProbeForRead(pluid, sizeof(LUID), 1);
1993         luidUser = *pluid;
1994     }
1995     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1996     {
1997         Status = _SEH2_GetExceptionCode();
1998         _SEH2_YIELD(goto Leave);
1999     }
2000     _SEH2_END;
2001 
2002     /* Reset the window station user LUID */
2003     RtlZeroMemory(&WindowStation->luidUser, sizeof(LUID));
2004 
2005     /* Reset the window station user SID */
2006     if (WindowStation->psidUser)
2007     {
2008         ExFreePoolWithTag(WindowStation->psidUser, USERTAG_SECURITY);
2009         WindowStation->psidUser = NULL;
2010     }
2011 
2012     /* Copy the new user SID if one has been provided */
2013     if (psid)
2014     {
2015         WindowStation->psidUser = ExAllocatePoolWithTag(PagedPool, size, USERTAG_SECURITY);
2016         if (WindowStation->psidUser == NULL)
2017         {
2018             EngSetLastError(ERROR_OUTOFMEMORY);
2019             goto Leave;
2020         }
2021 
2022         Status = STATUS_SUCCESS;
2023         _SEH2_TRY
2024         {
2025             ProbeForRead(psid, size, 1);
2026             RtlCopyMemory(WindowStation->psidUser, psid, size);
2027         }
2028         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2029         {
2030             Status = _SEH2_GetExceptionCode();
2031         }
2032         _SEH2_END;
2033 
2034         if (!NT_SUCCESS(Status))
2035         {
2036             ExFreePoolWithTag(WindowStation->psidUser, USERTAG_SECURITY);
2037             WindowStation->psidUser = NULL;
2038             goto Leave;
2039         }
2040     }
2041 
2042     /* Copy the new user LUID */
2043     WindowStation->luidUser = luidUser;
2044 
2045     Ret = TRUE;
2046 
2047 Leave:
2048     if (WindowStation)
2049         ObDereferenceObject(WindowStation);
2050 
2051     UserLeave();
2052     return Ret;
2053 }
2054 
2055 /* EOF */
2056