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