[WIN32K:NTUSER] In UserSetProcessWindowStation(), use a duplicated window station...
[reactos.git] / win32ss / user / ntuser / winsta.c
index 9711658..12a1b90 100644 (file)
@@ -14,7 +14,10 @@ DBG_DEFAULT_CHANNEL(UserWinsta);
 
 /* GLOBALS *******************************************************************/
 
-/* Currently active window station */
+/*
+ * The currently active window station. This is the
+ * only one interactive window station on the system.
+ */
 PWINSTATION_OBJECT InputWindowStation = NULL;
 
 /* Winlogon SAS window */
@@ -47,11 +50,11 @@ NTSTATUS
 NTAPI
 UserCreateWinstaDirectory(VOID)
 {
-    PPEB Peb;
     NTSTATUS Status;
-    WCHAR wstrWindowStationsDir[MAX_PATH];
+    PPEB Peb;
     OBJECT_ATTRIBUTES ObjectAttributes;
     HANDLE hWinstaDir;
+    WCHAR wstrWindowStationsDir[MAX_PATH];
 
     /* Create the WindowStations directory and cache its path for later use */
     Peb = NtCurrentPeb();
@@ -64,11 +67,14 @@ UserCreateWinstaDirectory(VOID)
     }
     else
     {
-        swprintf(wstrWindowStationsDir,
-                 L"%ws\\%lu%ws",
-                 SESSION_DIR,
-                 Peb->SessionId,
-                 WINSTA_OBJ_DIR);
+        Status = RtlStringCbPrintfW(wstrWindowStationsDir,
+                                    sizeof(wstrWindowStationsDir),
+                                    L"%ws\\%lu%ws",
+                                    SESSION_DIR,
+                                    Peb->SessionId,
+                                    WINSTA_OBJ_DIR);
+        if (!NT_SUCCESS(Status))
+            return Status;
 
         if (!RtlCreateUnicodeString(&gustrWindowStationsDir, wstrWindowStationsDir))
         {
@@ -78,10 +84,10 @@ UserCreateWinstaDirectory(VOID)
 
     InitializeObjectAttributes(&ObjectAttributes,
                                &gustrWindowStationsDir,
-                               0,
+                               OBJ_KERNEL_HANDLE,
                                NULL,
                                NULL);
-    Status = ZwCreateDirectoryObject(&hWinstaDir, 0, &ObjectAttributes);
+    Status = ZwCreateDirectoryObject(&hWinstaDir, DIRECTORY_CREATE_OBJECT, &ObjectAttributes);
     if (!NT_SUCCESS(Status))
     {
         ERR("Could not create %wZ directory (Status 0x%X)\n", &gustrWindowStationsDir,  Status);
@@ -93,17 +99,28 @@ UserCreateWinstaDirectory(VOID)
     return Status;
 }
 
-/* OBJECT CALLBACKS  **********************************************************/
+/* OBJECT CALLBACKS ***********************************************************/
 
 NTSTATUS
-APIENTRY
+NTAPI
 IntWinStaObjectDelete(
     _In_ PVOID Parameters)
 {
     PWIN32_DELETEMETHOD_PARAMETERS DeleteParameters = Parameters;
     PWINSTATION_OBJECT WinSta = (PWINSTATION_OBJECT)DeleteParameters->Object;
 
-    TRACE("Deleting window station (0x%p)\n", WinSta);
+    TRACE("Deleting window station 0x%p\n", WinSta);
+
+    if (WinSta == InputWindowStation)
+    {
+        ERR("WARNING: Deleting the interactive window station '%wZ'!\n",
+            &(OBJECT_HEADER_TO_NAME_INFO(OBJECT_TO_OBJECT_HEADER(InputWindowStation))->Name));
+
+        /* Only Winlogon can close and delete the interactive window station */
+        ASSERT(gpidLogon == PsGetCurrentProcessId());
+
+        InputWindowStation = NULL;
+    }
 
     WinSta->Flags |= WSS_DYING;
 
@@ -111,13 +128,11 @@ IntWinStaObjectDelete(
 
     RtlDestroyAtomTable(WinSta->AtomTable);
 
-    RtlFreeUnicodeString(&WinSta->Name);
-
     return STATUS_SUCCESS;
 }
 
 NTSTATUS
-APIENTRY
+NTAPI
 IntWinStaObjectParse(
     _In_ PVOID Parameters)
 {
@@ -183,7 +198,7 @@ IntWinStaObjectParse(
 
 NTSTATUS
 NTAPI
-IntWinstaOkToClose(
+IntWinStaOkToClose(
     _In_ PVOID Parameters)
 {
     PWIN32_OKAYTOCLOSEMETHOD_PARAMETERS OkToCloseParameters = Parameters;
@@ -191,7 +206,7 @@ IntWinstaOkToClose(
 
     ppi = PsGetCurrentProcessWin32Process();
 
-    if(ppi && (OkToCloseParameters->Handle == ppi->hwinsta))
+    if (ppi && (OkToCloseParameters->Handle == ppi->hwinsta))
     {
         return STATUS_ACCESS_DENIED;
     }
@@ -370,7 +385,7 @@ CheckWinstaAttributeAccess(ACCESS_MASK DesiredAccess)
  *    lpSecurity
  *       Security descriptor
  *
- *    Unknown3, Unknown4, Unknown5
+ *    Unknown3, Unknown4, Unknown5, Unknown6
  *       Unused
  *
  * Return Value
@@ -379,133 +394,390 @@ CheckWinstaAttributeAccess(ACCESS_MASK DesiredAccess)
  *    exists, the function succeeds and returns a handle to the existing
  *    window station. If the function fails, the return value is NULL.
  *
- * Todo
- *    Correct the prototype to match the Windows one (with 7 parameters
- *    on Windows XP).
- *
  * Status
  *    @implemented
  */
 
-HWINSTA APIENTRY
-NtUserCreateWindowStation(
-    POBJECT_ATTRIBUTES ObjectAttributes,
-    ACCESS_MASK dwDesiredAccess,
+NTSTATUS
+FASTCALL
+IntCreateWindowStation(
+    OUT HWINSTA* phWinSta,
+    IN POBJECT_ATTRIBUTES ObjectAttributes,
+    IN KPROCESSOR_MODE AccessMode,
+    IN KPROCESSOR_MODE OwnerMode,
+    IN ACCESS_MASK dwDesiredAccess,
     DWORD Unknown2,
     DWORD Unknown3,
     DWORD Unknown4,
     DWORD Unknown5,
     DWORD Unknown6)
 {
-    UNICODE_STRING WindowStationName;
-    PWINSTATION_OBJECT WindowStationObject;
-    HWINSTA WindowStation;
     NTSTATUS Status;
+    HWINSTA hWinSta;
+    PWINSTATION_OBJECT WindowStation;
 
-    TRACE("NtUserCreateWindowStation called\n");
+    TRACE("IntCreateWindowStation called\n");
+
+    ASSERT(phWinSta);
+    *phWinSta = NULL;
 
     Status = ObOpenObjectByName(ObjectAttributes,
                                 ExWindowStationObjectType,
-                                UserMode,
+                                AccessMode,
                                 NULL,
                                 dwDesiredAccess,
                                 NULL,
-                                (PVOID*)&WindowStation);
-
+                                (PVOID*)&hWinSta);
     if (NT_SUCCESS(Status))
     {
-        TRACE("NtUserCreateWindowStation opened window station %wZ\n", ObjectAttributes->ObjectName);
-        return (HWINSTA)WindowStation;
+        TRACE("IntCreateWindowStation opened window station '%wZ'\n",
+              ObjectAttributes->ObjectName);
+        *phWinSta = hWinSta;
+        return Status;
     }
 
     /*
-     * No existing window station found, try to create new one
+     * No existing window station found, try to create a new one.
      */
 
-    /* Capture window station name */
-    _SEH2_TRY
-    {
-        ProbeForRead( ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), 1);
-        Status = IntSafeCopyUnicodeStringTerminateNULL(&WindowStationName, ObjectAttributes->ObjectName);
-    }
-    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
-    {
-        Status =_SEH2_GetExceptionCode();
-    }
-    _SEH2_END
-
-    if (! NT_SUCCESS(Status))
-    {
-        ERR("Failed reading capturing window station name\n");
-        SetLastNtError(Status);
-        return NULL;
-    }
-
     /* Create the window station object */
-    Status = ObCreateObject(UserMode,
+    Status = ObCreateObject(AccessMode,
                             ExWindowStationObjectType,
                             ObjectAttributes,
-                            UserMode,
+                            OwnerMode,
                             NULL,
                             sizeof(WINSTATION_OBJECT),
                             0,
                             0,
-                            (PVOID*)&WindowStationObject);
-
+                            (PVOID*)&WindowStation);
     if (!NT_SUCCESS(Status))
     {
-        ERR("ObCreateObject failed with %lx for window station %wZ\n", Status, &WindowStationName);
-        ExFreePoolWithTag(WindowStationName.Buffer, TAG_STRING);
-        SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
-        return 0;
+        ERR("ObCreateObject failed for window station '%wZ', Status 0x%08lx\n",
+            ObjectAttributes->ObjectName, Status);
+        SetLastNtError(Status);
+        return Status;
     }
 
     /* Initialize the window station */
-    RtlZeroMemory(WindowStationObject, sizeof(WINSTATION_OBJECT));
+    RtlZeroMemory(WindowStation, sizeof(WINSTATION_OBJECT));
 
-    InitializeListHead(&WindowStationObject->DesktopListHead);
-    WindowStationObject->Name = WindowStationName;
-    WindowStationObject->dwSessionId = NtCurrentPeb()->SessionId;
-    Status = RtlCreateAtomTable(37, &WindowStationObject->AtomTable);
+    InitializeListHead(&WindowStation->DesktopListHead);
+    WindowStation->dwSessionId = NtCurrentPeb()->SessionId;
+    Status = RtlCreateAtomTable(37, &WindowStation->AtomTable);
     if (!NT_SUCCESS(Status))
     {
-        ERR("RtlCreateAtomTable failed with %lx for window station %wZ\n", Status, &WindowStationName);
-        ObDereferenceObject(WindowStationObject);
-        SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
-        return 0;
+        ERR("RtlCreateAtomTable failed for window station '%wZ', Status 0x%08lx\n",
+            ObjectAttributes->ObjectName, Status);
+        ObDereferenceObject(WindowStation);
+        SetLastNtError(Status);
+        return Status;
     }
 
-    Status = ObInsertObject((PVOID)WindowStationObject,
+    Status = ObInsertObject(WindowStation,
                             NULL,
                             dwDesiredAccess,
                             0,
                             NULL,
-                            (PVOID*)&WindowStation);
-
+                            (PVOID*)&hWinSta);
     if (!NT_SUCCESS(Status))
     {
-        ERR("ObInsertObject failed with %lx for window station\n", Status);
-        SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
-        return 0;
+        ERR("ObInsertObject failed for window station, Status 0x%08lx\n", Status);
+        SetLastNtError(Status);
+        return Status;
     }
 
+    // FIXME! TODO: Add this new window station to a linked list
+
     if (InputWindowStation == NULL)
     {
         ERR("Initializing input window station\n");
-        InputWindowStation = WindowStationObject;
 
-        WindowStationObject->Flags &= ~WSS_NOIO;
+        /* Only Winlogon can create the interactive window station */
+        ASSERT(gpidLogon == PsGetCurrentProcessId());
 
+        InputWindowStation = WindowStation;
+        WindowStation->Flags &= ~WSS_NOIO;
         InitCursorImpl();
     }
     else
     {
-        WindowStationObject->Flags |= WSS_NOIO;
+        WindowStation->Flags |= WSS_NOIO;
     }
 
-    TRACE("NtUserCreateWindowStation created object %p with name %wZ handle %p\n",
-          WindowStation, &WindowStationObject->Name, WindowStation);
-    return WindowStation;
+    TRACE("IntCreateWindowStation created window station '%wZ' object 0x%p handle 0x%p\n",
+          ObjectAttributes->ObjectName, WindowStation, hWinSta);
+
+    *phWinSta = hWinSta;
+    return STATUS_SUCCESS;
+}
+
+static VOID
+FreeUserModeWindowStationName(
+    IN OUT PUNICODE_STRING WindowStationName,
+    IN PUNICODE_STRING TebStaticUnicodeString,
+    IN OUT POBJECT_ATTRIBUTES UserModeObjectAttributes OPTIONAL,
+    IN POBJECT_ATTRIBUTES LocalObjectAttributes OPTIONAL)
+{
+    SIZE_T MemSize = 0;
+
+    /* Try to restore the user's UserModeObjectAttributes */
+    if (UserModeObjectAttributes && LocalObjectAttributes)
+    {
+        _SEH2_TRY
+        {
+            ProbeForWrite(UserModeObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG));
+            *UserModeObjectAttributes = *LocalObjectAttributes;
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            NOTHING;
+        }
+        _SEH2_END;
+    }
+
+    /* Free the user-mode memory */
+    if (WindowStationName && (WindowStationName != TebStaticUnicodeString))
+    {
+        ZwFreeVirtualMemory(ZwCurrentProcess(),
+                            (PVOID*)&WindowStationName,
+                            &MemSize,
+                            MEM_RELEASE);
+    }
+}
+
+static NTSTATUS
+BuildUserModeWindowStationName(
+    IN OUT POBJECT_ATTRIBUTES UserModeObjectAttributes,
+    IN OUT POBJECT_ATTRIBUTES LocalObjectAttributes,
+    OUT PUNICODE_STRING* WindowStationName,
+    OUT PUNICODE_STRING* TebStaticUnicodeString)
+{
+    NTSTATUS Status;
+    SIZE_T MemSize;
+
+    LUID CallerLuid;
+    PTEB Teb;
+    USHORT StrSize;
+
+    *WindowStationName = NULL;
+    *TebStaticUnicodeString = NULL;
+
+    /* Retrieve the current process LUID */
+    Status = GetProcessLuid(NULL, NULL, &CallerLuid);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("Failed to retrieve the caller LUID, Status 0x%08lx\n", Status);
+        return Status;
+    }
+
+    /* Compute the needed string size */
+    MemSize = _scwprintf(L"%wZ\\Service-0x%x-%x$",
+                         &gustrWindowStationsDir,
+                         CallerLuid.HighPart,
+                         CallerLuid.LowPart);
+    MemSize = MemSize * sizeof(WCHAR) + sizeof(UNICODE_NULL);
+    if (MemSize > MAXUSHORT)
+    {
+        ERR("Window station name length is too long.\n");
+        return STATUS_NAME_TOO_LONG;
+    }
+    StrSize = (USHORT)MemSize;
+
+    /*
+     * Check whether it's short enough so that we can use the static buffer
+     * in the TEB. Otherwise continue with virtual memory allocation.
+     */
+    Teb = NtCurrentTeb();
+    if (Teb && (StrSize <= sizeof(Teb->StaticUnicodeBuffer)))
+    {
+        /* We can use the TEB's static unicode string */
+        ASSERT(Teb->StaticUnicodeString.Buffer == Teb->StaticUnicodeBuffer);
+        ASSERT(Teb->StaticUnicodeString.MaximumLength == sizeof(Teb->StaticUnicodeBuffer));
+
+        /* Remember the TEB's static unicode string address for later */
+        *TebStaticUnicodeString = &Teb->StaticUnicodeString;
+
+        *WindowStationName = *TebStaticUnicodeString;
+        (*WindowStationName)->Length = 0;
+    }
+    else
+    {
+        /* The TEB's static unicode string is too small, allocate some user-mode virtual memory */
+        MemSize += ALIGN_UP(sizeof(UNICODE_STRING), sizeof(PVOID));
+
+        /* Allocate the memory in user-mode */
+        Status = ZwAllocateVirtualMemory(ZwCurrentProcess(),
+                                         (PVOID*)WindowStationName,
+                                         0,
+                                         &MemSize,
+                                         MEM_COMMIT,
+                                         PAGE_READWRITE);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("ZwAllocateVirtualMemory() failed, Status 0x%08lx\n", Status);
+            return Status;
+        }
+
+        RtlInitEmptyUnicodeString(*WindowStationName,
+                                  (PWCHAR)((ULONG_PTR)*WindowStationName +
+                                      ALIGN_UP(sizeof(UNICODE_STRING), sizeof(PVOID))),
+                                  StrSize);
+    }
+
+    /* Build a valid window station name from the LUID */
+    Status = RtlStringCbPrintfW((*WindowStationName)->Buffer,
+                                (*WindowStationName)->MaximumLength,
+                                L"%wZ\\Service-0x%x-%x$",
+                                &gustrWindowStationsDir,
+                                CallerLuid.HighPart,
+                                CallerLuid.LowPart);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("Impossible to build a valid window station name, Status 0x%08lx\n", Status);
+        goto Quit;
+    }
+    (*WindowStationName)->Length = (USHORT)(wcslen((*WindowStationName)->Buffer) * sizeof(WCHAR));
+
+    /* Try to update the user's UserModeObjectAttributes */
+    _SEH2_TRY
+    {
+        ProbeForWrite(UserModeObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG));
+        *LocalObjectAttributes = *UserModeObjectAttributes;
+
+        UserModeObjectAttributes->ObjectName = *WindowStationName;
+        UserModeObjectAttributes->RootDirectory = NULL;
+
+        Status = STATUS_SUCCESS;
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status = _SEH2_GetExceptionCode();
+    }
+    _SEH2_END;
+
+Quit:
+    if (!NT_SUCCESS(Status))
+    {
+        /* Release the window station name */
+        FreeUserModeWindowStationName(*WindowStationName,
+                                      *TebStaticUnicodeString,
+                                      NULL, NULL);
+    }
+
+    return Status;
+}
+
+HWINSTA
+APIENTRY
+NtUserCreateWindowStation(
+    IN POBJECT_ATTRIBUTES ObjectAttributes,
+    IN ACCESS_MASK dwDesiredAccess,
+    DWORD Unknown2,
+    DWORD Unknown3,
+    DWORD Unknown4,
+    DWORD Unknown5,
+    DWORD Unknown6)
+{
+    NTSTATUS Status = STATUS_SUCCESS;
+    HWINSTA hWinSta = NULL;
+    OBJECT_ATTRIBUTES LocalObjectAttributes;
+    PUNICODE_STRING WindowStationName = NULL;
+    PUNICODE_STRING TebStaticUnicodeString = NULL;
+    KPROCESSOR_MODE OwnerMode = UserMode;
+
+    TRACE("NtUserCreateWindowStation called\n");
+
+    /* Capture the object attributes and the window station name */
+    _SEH2_TRY
+    {
+        ProbeForRead(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG));
+        LocalObjectAttributes = *ObjectAttributes;
+        if (LocalObjectAttributes.Length != sizeof(OBJECT_ATTRIBUTES))
+        {
+            ERR("Invalid ObjectAttributes length!\n");
+            Status = STATUS_INVALID_PARAMETER;
+            _SEH2_LEAVE;
+        }
+
+        /*
+         * Check whether the caller provided a window station name together
+         * with a RootDirectory handle.
+         *
+         * If the caller did not provide a window station name, build a new one
+         * based on the logon session identifier for the calling process.
+         * The new name is allocated in user-mode, as the rest of ObjectAttributes
+         * already is, so that the validation performed by the Object Manager
+         * can be done adequately.
+         */
+        if ((LocalObjectAttributes.ObjectName == NULL ||
+             LocalObjectAttributes.ObjectName->Buffer == NULL ||
+             LocalObjectAttributes.ObjectName->Length == 0 ||
+             LocalObjectAttributes.ObjectName->Buffer[0] == UNICODE_NULL)
+            /* &&
+            LocalObjectAttributes.RootDirectory == NULL */)
+        {
+            /* No, build the new window station name */
+            Status = BuildUserModeWindowStationName(ObjectAttributes,
+                                                    &LocalObjectAttributes,
+                                                    &WindowStationName,
+                                                    &TebStaticUnicodeString);
+            if (!NT_SUCCESS(Status))
+            {
+                ERR("BuildUserModeWindowStationName() failed, Status 0x%08lx\n", Status);
+                _SEH2_LEAVE;
+            }
+            OwnerMode = KernelMode;
+        }
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status =_SEH2_GetExceptionCode();
+        ERR("ObjectAttributes capture failed, Status 0x%08lx\n", Status);
+    }
+    _SEH2_END;
+
+    if (!NT_SUCCESS(Status))
+    {
+        SetLastNtError(Status);
+        return NULL;
+    }
+
+    /* Create the window station */
+    Status = IntCreateWindowStation(&hWinSta,
+                                    ObjectAttributes,
+                                    UserMode,
+                                    OwnerMode,
+                                    dwDesiredAccess,
+                                    Unknown2,
+                                    Unknown3,
+                                    Unknown4,
+                                    Unknown5,
+                                    Unknown6);
+    if (NT_SUCCESS(Status))
+    {
+        TRACE("NtUserCreateWindowStation created window station '%wZ' with handle 0x%p\n",
+              ObjectAttributes->ObjectName, hWinSta);
+    }
+    else
+    {
+        ASSERT(hWinSta == NULL);
+        ERR("NtUserCreateWindowStation failed to create window station '%wZ', Status 0x%08lx\n",
+            ObjectAttributes->ObjectName, Status);
+    }
+
+    /* Try to restore the user's ObjectAttributes and release the window station name */
+    FreeUserModeWindowStationName(WindowStationName,
+                                  TebStaticUnicodeString,
+                                  (OwnerMode == KernelMode ? ObjectAttributes : NULL),
+                                  &LocalObjectAttributes);
+
+    if (!NT_SUCCESS(Status))
+    {
+        ASSERT(hWinSta == NULL);
+        SetLastNtError(Status);
+    }
+
+    return hWinSta;
 }
 
 /*
@@ -532,32 +804,130 @@ NtUserCreateWindowStation(
  *    @implemented
  */
 
-HWINSTA APIENTRY
+HWINSTA
+APIENTRY
 NtUserOpenWindowStation(
-    POBJECT_ATTRIBUTES ObjectAttributes,
-    ACCESS_MASK dwDesiredAccess)
+    IN POBJECT_ATTRIBUTES ObjectAttributes,
+    IN ACCESS_MASK dwDesiredAccess)
 {
-    HWINSTA hwinsta;
-    NTSTATUS Status;
+    NTSTATUS Status = STATUS_SUCCESS;
+    HWINSTA hWinSta = NULL;
+    OBJECT_ATTRIBUTES LocalObjectAttributes;
+    PUNICODE_STRING WindowStationName = NULL;
+    PUNICODE_STRING TebStaticUnicodeString = NULL;
+    KPROCESSOR_MODE OwnerMode = UserMode;
+
+    TRACE("NtUserOpenWindowStation called\n");
 
+    /* Capture the object attributes and the window station name */
+    _SEH2_TRY
+    {
+        ProbeForRead(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG));
+        LocalObjectAttributes = *ObjectAttributes;
+        if (LocalObjectAttributes.Length != sizeof(OBJECT_ATTRIBUTES))
+        {
+            ERR("Invalid ObjectAttributes length!\n");
+            Status = STATUS_INVALID_PARAMETER;
+            _SEH2_LEAVE;
+        }
+
+        /*
+         * Check whether the caller did not provide a window station name,
+         * or provided the special "Service-0x00000000-00000000$" name.
+         *
+         * NOTE: On Windows, the special "Service-0x00000000-00000000$" string
+         * is used instead of an empty name (observed when API-monitoring
+         * OpenWindowStation() called with an empty window station name).
+         */
+        if ((LocalObjectAttributes.ObjectName == NULL ||
+             LocalObjectAttributes.ObjectName->Buffer == NULL ||
+             LocalObjectAttributes.ObjectName->Length == 0 ||
+             LocalObjectAttributes.ObjectName->Buffer[0] == UNICODE_NULL)
+            /* &&
+            LocalObjectAttributes.RootDirectory == NULL */)
+        {
+            /* No, remember that for later */
+            LocalObjectAttributes.ObjectName = NULL;
+        }
+        if (LocalObjectAttributes.ObjectName &&
+            LocalObjectAttributes.ObjectName->Length ==
+                sizeof(L"Service-0x00000000-00000000$") - sizeof(UNICODE_NULL) &&
+            _wcsnicmp(LocalObjectAttributes.ObjectName->Buffer,
+                      L"Service-0x00000000-00000000$",
+                      LocalObjectAttributes.ObjectName->Length / sizeof(WCHAR)) == 0)
+        {
+            /* No, remember that for later */
+            LocalObjectAttributes.ObjectName = NULL;
+        }
+
+        /*
+         * If the caller did not provide a window station name, build a new one
+         * based on the logon session identifier for the calling process.
+         * The new name is allocated in user-mode, as the rest of ObjectAttributes
+         * already is, so that the validation performed by the Object Manager
+         * can be done adequately.
+         */
+        if (!LocalObjectAttributes.ObjectName)
+        {
+            /* No, build the new window station name */
+            Status = BuildUserModeWindowStationName(ObjectAttributes,
+                                                    &LocalObjectAttributes,
+                                                    &WindowStationName,
+                                                    &TebStaticUnicodeString);
+            if (!NT_SUCCESS(Status))
+            {
+                ERR("BuildUserModeWindowStationName() failed, Status 0x%08lx\n", Status);
+                _SEH2_LEAVE;
+            }
+            OwnerMode = KernelMode;
+        }
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status =_SEH2_GetExceptionCode();
+        ERR("ObjectAttributes capture failed, Status 0x%08lx\n", Status);
+    }
+    _SEH2_END;
+
+    if (!NT_SUCCESS(Status))
+    {
+        SetLastNtError(Status);
+        return NULL;
+    }
+
+    /* Open the window station */
     Status = ObOpenObjectByName(ObjectAttributes,
                                 ExWindowStationObjectType,
                                 UserMode,
                                 NULL,
                                 dwDesiredAccess,
                                 NULL,
-                                (PVOID*)&hwinsta);
+                                (PVOID*)&hWinSta);
+    if (NT_SUCCESS(Status))
+    {
+        TRACE("NtUserOpenWindowStation opened window station '%wZ' with handle 0x%p\n",
+              ObjectAttributes->ObjectName, hWinSta);
+    }
+    else
+    {
+        ASSERT(hWinSta == NULL);
+        ERR("NtUserOpenWindowStation failed to open window station '%wZ', Status 0x%08lx\n",
+            ObjectAttributes->ObjectName, Status);
+    }
+
+    /* Try to restore the user's ObjectAttributes and release the window station name */
+    FreeUserModeWindowStationName(WindowStationName,
+                                  TebStaticUnicodeString,
+                                  (OwnerMode == KernelMode ? ObjectAttributes : NULL),
+                                  &LocalObjectAttributes);
 
     if (!NT_SUCCESS(Status))
     {
-        ERR("NtUserOpenWindowStation failed\n");
+        ASSERT(hWinSta == NULL);
         SetLastNtError(Status);
-        return 0;
     }
 
-    TRACE("Opened window station %wZ with handle %p\n", ObjectAttributes->ObjectName, hwinsta);
-
-    return hwinsta;
+    return hWinSta;
 }
 
 /*
@@ -574,7 +944,7 @@ NtUserOpenWindowStation(
  *
  * Remarks
  *    The window station handle can be created with NtUserCreateWindowStation
- *    or NtUserOpenWindowStation. Attemps to close a handle to the window
+ *    or NtUserOpenWindowStation. Attempts to close a handle to the window
  *    station assigned to the calling process will fail.
  *
  * Status
@@ -601,8 +971,7 @@ NtUserCloseWindowStation(
                                             UserMode,
                                             0,
                                             &Object,
-                                            0);
-
+                                            NULL);
     if (!NT_SUCCESS(Status))
     {
         ERR("Validation of window station handle (%p) failed\n", hWinSta);
@@ -671,11 +1040,16 @@ NtUserGetObjectInformation(
     DWORD nLength,
     PDWORD nLengthNeeded)
 {
-    PWINSTATION_OBJECT WinStaObject;
-    PDESKTOP DesktopObject = NULL;
     NTSTATUS Status;
+    PWINSTATION_OBJECT WinStaObject = NULL;
+    PDESKTOP DesktopObject = NULL;
+    POBJECT_HEADER ObjectHeader;
+    POBJECT_HEADER_NAME_INFO NameInfo;
+    OBJECT_HANDLE_INFORMATION HandleInfo;
+    USEROBJECTFLAGS ObjectFlags;
+    PUNICODE_STRING pStrNameU = NULL;
     PVOID pvData = NULL;
-    DWORD nDataSize = 0;
+    SIZE_T nDataSize = 0;
 
     _SEH2_TRY
     {
@@ -690,18 +1064,18 @@ NtUserGetObjectInformation(
     }
     _SEH2_END;
 
-    /* try windowstation */
-    TRACE("Trying to open window station %p\n", hObject);
+    /* Try window station */
+    TRACE("Trying to open window station 0x%p\n", hObject);
     Status = ObReferenceObjectByHandle(hObject,
                                        0,
                                        ExWindowStationObjectType,
                                        UserMode,
                                        (PVOID*)&WinStaObject,
-                                       NULL);
+                                       &HandleInfo);
 
     if (Status == STATUS_OBJECT_TYPE_MISMATCH)
     {
-        /* try desktop */
+        /* Try desktop */
         TRACE("Trying to open desktop %p\n", hObject);
         WinStaObject = NULL;
         Status = IntValidateDesktopHandle(hObject,
@@ -716,21 +1090,57 @@ NtUserGetObjectInformation(
         goto Exit;
     }
 
-    TRACE("WinSta or Desktop opened!!\n");
+    TRACE("WinSta or Desktop opened!\n");
 
-    /* get data */
+    /* Get data */
     switch (nIndex)
     {
         case UOI_FLAGS:
-            Status = STATUS_NOT_IMPLEMENTED;
-            ERR("UOI_FLAGS unimplemented!\n");
+        {
+            ObjectFlags.fReserved = FALSE;
+            ObjectFlags.fInherit = !!(HandleInfo.HandleAttributes & OBJ_INHERIT);
+
+            ObjectFlags.dwFlags = 0;
+            if (WinStaObject != NULL)
+            {
+                if (!(WinStaObject->Flags & WSS_NOIO))
+                    ObjectFlags.dwFlags |= WSF_VISIBLE;
+            }
+            else if (DesktopObject != NULL)
+            {
+                FIXME("Setting DF_ALLOWOTHERACCOUNTHOOK is unimplemented.\n");
+            }
+            else
+            {
+                ERR("No associated WinStaObject nor DesktopObject!\n");
+            }
+
+            pvData = &ObjectFlags;
+            nDataSize = sizeof(ObjectFlags);
+            Status = STATUS_SUCCESS;
             break;
+        }
 
         case UOI_NAME:
+        {
             if (WinStaObject != NULL)
             {
-                pvData = WinStaObject->Name.Buffer;
-                nDataSize = WinStaObject->Name.Length + sizeof(WCHAR);
+                ObjectHeader = OBJECT_TO_OBJECT_HEADER(WinStaObject);
+                NameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
+
+                if (NameInfo && (NameInfo->Name.Length > 0))
+                {
+                    /* Named window station */
+                    pStrNameU = &NameInfo->Name;
+                    nDataSize = pStrNameU->Length + sizeof(UNICODE_NULL);
+                }
+                else
+                {
+                    /* Unnamed window station (should never happen!) */
+                    ASSERT(FALSE);
+                    pStrNameU = NULL;
+                    nDataSize = sizeof(UNICODE_NULL);
+                }
                 Status = STATUS_SUCCESS;
             }
             else if (DesktopObject != NULL)
@@ -740,25 +1150,34 @@ NtUserGetObjectInformation(
                 Status = STATUS_SUCCESS;
             }
             else
+            {
                 Status = STATUS_INVALID_PARAMETER;
+            }
             break;
+        }
 
         case UOI_TYPE:
+        {
             if (WinStaObject != NULL)
             {
-                pvData = L"WindowStation";
-                nDataSize = sizeof(L"WindowStation");
+                ObjectHeader = OBJECT_TO_OBJECT_HEADER(WinStaObject);
+                pStrNameU = &ObjectHeader->Type->Name;
+                nDataSize = pStrNameU->Length + sizeof(UNICODE_NULL);
                 Status = STATUS_SUCCESS;
             }
             else if (DesktopObject != NULL)
             {
-                pvData = L"Desktop";
-                nDataSize = sizeof(L"Desktop");
+                ObjectHeader = OBJECT_TO_OBJECT_HEADER(DesktopObject);
+                pStrNameU = &ObjectHeader->Type->Name;
+                nDataSize = pStrNameU->Length + sizeof(UNICODE_NULL);
                 Status = STATUS_SUCCESS;
             }
             else
+            {
                 Status = STATUS_INVALID_PARAMETER;
+            }
             break;
+        }
 
         case UOI_USER_SID:
             Status = STATUS_NOT_IMPLEMENTED;
@@ -771,7 +1190,7 @@ NtUserGetObjectInformation(
     }
 
 Exit:
-    if (Status == STATUS_SUCCESS && nLength < nDataSize)
+    if ((Status == STATUS_SUCCESS) && (nLength < nDataSize))
         Status = STATUS_BUFFER_TOO_SMALL;
 
     _SEH2_TRY
@@ -779,11 +1198,26 @@ Exit:
         if (nLengthNeeded)
             *nLengthNeeded = nDataSize;
 
-        /* try to copy data to caller */
-        if (Status == STATUS_SUCCESS)
+        /* Try to copy data to caller */
+        if (Status == STATUS_SUCCESS && (nDataSize > 0))
         {
             TRACE("Trying to copy data to caller (len = %lu, len needed = %lu)\n", nLength, nDataSize);
-            RtlCopyMemory(pvInformation, pvData, nDataSize);
+            if (pvData)
+            {
+                /* Copy the data */
+                RtlCopyMemory(pvInformation, pvData, nDataSize);
+            }
+            else if (pStrNameU)
+            {
+                /* Copy and NULL-terminate the string */
+                RtlCopyMemory(pvInformation, pStrNameU->Buffer, pStrNameU->Length);
+                ((PWCHAR)pvInformation)[pStrNameU->Length / sizeof(WCHAR)] = UNICODE_NULL;
+            }
+            else
+            {
+                /* Zero the memory */
+                RtlZeroMemory(pvInformation, nDataSize);
+            }
         }
     }
     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
@@ -792,11 +1226,11 @@ Exit:
     }
     _SEH2_END;
 
-    /* release objects */
-    if (WinStaObject != NULL)
-        ObDereferenceObject(WinStaObject);
+    /* Release objects */
     if (DesktopObject != NULL)
         ObDereferenceObject(DesktopObject);
+    if (WinStaObject != NULL)
+        ObDereferenceObject(WinStaObject);
 
     if (!NT_SUCCESS(Status))
     {
@@ -852,8 +1286,6 @@ NtUserSetObjectInformation(
 }
 
 
-
-
 HWINSTA FASTCALL
 UserGetProcessWindowStation(VOID)
 {
@@ -886,16 +1318,16 @@ NtUserGetProcessWindowStation(VOID)
 BOOL FASTCALL
 UserSetProcessWindowStation(HWINSTA hWindowStation)
 {
-    PPROCESSINFO ppi;
     NTSTATUS Status;
-    HWINSTA hwinstaOld;
+    PPROCESSINFO ppi;
     OBJECT_HANDLE_INFORMATION ObjectHandleInfo;
     PWINSTATION_OBJECT NewWinSta = NULL, OldWinSta;
+    HWINSTA hCacheWinSta;
 
     ppi = PsGetCurrentProcessWin32Process();
 
     /* Reference the new window station */
-    if(hWindowStation !=NULL)
+    if (hWindowStation != NULL)
     {
         Status = IntValidateWindowStationHandle(hWindowStation,
                                                 UserMode,
@@ -904,33 +1336,79 @@ UserSetProcessWindowStation(HWINSTA hWindowStation)
                                                 &ObjectHandleInfo);
         if (!NT_SUCCESS(Status))
         {
-            TRACE("Validation of window station handle (%p) failed\n",
-                  hWindowStation);
+            TRACE("Validation of window station handle 0x%p failed\n", hWindowStation);
             SetLastNtError(Status);
             return FALSE;
         }
     }
 
     OldWinSta = ppi->prpwinsta;
-    hwinstaOld = PsGetProcessWin32WindowStation(ppi->peProcess);
+    hCacheWinSta = PsGetProcessWin32WindowStation(ppi->peProcess);
 
     /* Dereference the previous window station */
-    if(OldWinSta != NULL)
+    if (OldWinSta != NULL)
     {
         ObDereferenceObject(OldWinSta);
     }
 
-    /* Check if we have a stale handle (it should happen for console apps) */
-    if(hwinstaOld != ppi->hwinsta)
-    {
-        ObCloseHandle(hwinstaOld, UserMode);
-    }
-
     /*
-     * FIXME: Don't allow changing the window station if there are threads that are attached to desktops and own GUI objects.
+     * FIXME: Don't allow changing the window station if there are threads that are attached to desktops and own GUI objects?
      */
 
-    PsSetProcessWindowStation(ppi->peProcess, hWindowStation);
+    /* Close the cached EPROCESS window station handle if needed */
+    if (hCacheWinSta != NULL)
+    {
+        /* Reference the window station */
+        Status = ObReferenceObjectByHandle(hCacheWinSta,
+                                           0,
+                                           ExWindowStationObjectType,
+                                           UserMode,
+                                           (PVOID*)&OldWinSta,
+                                           NULL);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("Failed to reference the inherited window station, Status 0x%08lx\n", Status);
+            /* We failed, reset the cache */
+            hCacheWinSta = NULL;
+            PsSetProcessWindowStation(ppi->peProcess, hCacheWinSta);
+        }
+        else
+        {
+            /*
+             * Close the old handle and reset the cache only
+             * if we are setting a different window station.
+             */
+            if (NewWinSta != OldWinSta)
+            {
+                ObCloseHandle(hCacheWinSta, UserMode);
+                hCacheWinSta = NULL;
+                PsSetProcessWindowStation(ppi->peProcess, hCacheWinSta);
+            }
+
+            /* Dereference the window station */
+            ObDereferenceObject(OldWinSta);
+        }
+    }
+
+    /* Duplicate and save a new cached EPROCESS window station handle */
+    if ((hCacheWinSta == NULL) && (hWindowStation != NULL))
+    {
+        Status = ZwDuplicateObject(ZwCurrentProcess(),
+                                   hWindowStation,
+                                   ZwCurrentProcess(),
+                                   (PHANDLE)&hCacheWinSta,
+                                   0,
+                                   0,
+                                   DUPLICATE_SAME_ACCESS);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("UserSetProcessWindowStation: Failed to duplicate the window station handle, Status 0x%08lx\n", Status);
+        }
+        else
+        {
+            PsSetProcessWindowStation(ppi->peProcess, hCacheWinSta);
+        }
+    }
 
     ppi->prpwinsta = NewWinSta;
     ppi->hwinsta = hWindowStation;
@@ -946,11 +1424,11 @@ UserSetProcessWindowStation(HWINSTA hWindowStation)
         ppi->W32PF_flags &= ~W32PF_READSCREENACCESSGRANTED;
     }
 
-    if (NewWinSta && !(NewWinSta->Flags & WSS_NOIO) )
+    if (NewWinSta && !(NewWinSta->Flags & WSS_NOIO))
     {
         ppi->W32PF_flags |= W32PF_IOWINSTA;
     }
-    else // Might be closed if the handle is null.
+    else /* Might be closed if the handle is NULL */
     {
         ppi->W32PF_flags &= ~W32PF_IOWINSTA;
     }
@@ -1016,7 +1494,7 @@ NtUserLockWindowStation(HWINSTA hWindowStation)
                                             UserMode,
                                             0,
                                             &Object,
-                                            0);
+                                            NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("Validation of window station handle (%p) failed\n",
@@ -1061,7 +1539,7 @@ NtUserUnlockWindowStation(HWINSTA hWindowStation)
                                             UserMode,
                                             0,
                                             &Object,
-                                            0);
+                                            NULL);
     if (!NT_SUCCESS(Status))
     {
         TRACE("Validation of window station handle (%p) failed\n",
@@ -1092,6 +1570,14 @@ BuildWindowStationNameList(
     POBJECT_DIRECTORY_INFORMATION DirEntry;
     WCHAR NullWchar;
 
+    //
+    // FIXME: Fully wrong! Since, by calling NtUserCreateWindowStation
+    // with judicious parameters one can create window stations elsewhere
+    // than in Windows\WindowStations directory, Win32k definitely MUST
+    // maintain a list of window stations it has created, and not rely
+    // on the enumeration of Windows\WindowStations !!!
+    //
+
     /*
      * Try to open the directory.
      */
@@ -1277,7 +1763,7 @@ BuildDesktopNameList(
                                             UserMode,
                                             0,
                                             &WindowStation,
-                                            0);
+                                            NULL);
     if (! NT_SUCCESS(Status))
     {
         return Status;
@@ -1441,16 +1927,18 @@ NtUserLockWorkStation(VOID)
     return ret;
 }
 
-BOOL APIENTRY
+BOOL
+NTAPI
 NtUserSetWindowStationUser(
-    HWINSTA hWindowStation,
-    PLUID pluid,
-    PSID psid,
-    DWORD size)
+    IN HWINSTA hWindowStation,
+    IN PLUID pluid,
+    IN PSID psid OPTIONAL,
+    IN DWORD size)
 {
+    BOOL Ret = FALSE;
     NTSTATUS Status;
     PWINSTATION_OBJECT WindowStation = NULL;
-    BOOL Ret = FALSE;
+    LUID luidUser;
 
     UserEnterExclusive();
 
@@ -1460,56 +1948,81 @@ NtUserSetWindowStationUser(
         goto Leave;
     }
 
+    /* Validate the window station */
     Status = IntValidateWindowStationHandle(hWindowStation,
                                             UserMode,
                                             0,
                                             &WindowStation,
-                                            0);
+                                            NULL);
     if (!NT_SUCCESS(Status))
     {
         goto Leave;
     }
 
-    if (WindowStation->psidUser)
-    {
-        ExFreePoolWithTag(WindowStation->psidUser, USERTAG_SECURITY);
-    }
-
-    WindowStation->psidUser = ExAllocatePoolWithTag(PagedPool, size, USERTAG_SECURITY);
-    if (WindowStation->psidUser == NULL)
-    {
-        EngSetLastError(ERROR_OUTOFMEMORY);
-        goto Leave;
-    }
-
+    /* Capture the user LUID */
     _SEH2_TRY
     {
-        ProbeForRead( psid, size, 1);
-        ProbeForRead( pluid, sizeof(LUID), 1);
-
-        RtlCopyMemory(WindowStation->psidUser, psid, size);
-        WindowStation->luidUser = *pluid;
+        ProbeForRead(pluid, sizeof(LUID), 1);
+        luidUser = *pluid;
     }
     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
         Status = _SEH2_GetExceptionCode();
+        _SEH2_YIELD(goto Leave);
     }
     _SEH2_END;
 
-    if (!NT_SUCCESS(Status))
+    /* Reset the window station user LUID */
+    RtlZeroMemory(&WindowStation->luidUser, sizeof(LUID));
+
+    /* Reset the window station user SID */
+    if (WindowStation->psidUser)
     {
         ExFreePoolWithTag(WindowStation->psidUser, USERTAG_SECURITY);
-        WindowStation->psidUser = 0;
-        goto Leave;
+        WindowStation->psidUser = NULL;
     }
 
+    /* Copy the new user SID if one has been provided */
+    if (psid)
+    {
+        WindowStation->psidUser = ExAllocatePoolWithTag(PagedPool, size, USERTAG_SECURITY);
+        if (WindowStation->psidUser == NULL)
+        {
+            EngSetLastError(ERROR_OUTOFMEMORY);
+            goto Leave;
+        }
+
+        Status = STATUS_SUCCESS;
+        _SEH2_TRY
+        {
+            ProbeForRead(psid, size, 1);
+            RtlCopyMemory(WindowStation->psidUser, psid, size);
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            Status = _SEH2_GetExceptionCode();
+        }
+        _SEH2_END;
+
+        if (!NT_SUCCESS(Status))
+        {
+            ExFreePoolWithTag(WindowStation->psidUser, USERTAG_SECURITY);
+            WindowStation->psidUser = NULL;
+            goto Leave;
+        }
+    }
+
+    /* Copy the new user LUID */
+    WindowStation->luidUser = luidUser;
+
     Ret = TRUE;
 
 Leave:
-    if (WindowStation) ObDereferenceObject(WindowStation);
+    if (WindowStation)
+        ObDereferenceObject(WindowStation);
+
     UserLeave();
     return Ret;
 }
 
-
 /* EOF */