[WIN32K:NTUSER] Make NtUserResolveDesktop() and IntResolveDesktop() work in a more...
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Sun, 22 Jul 2018 18:38:26 +0000 (20:38 +0200)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Sun, 19 Aug 2018 20:18:37 +0000 (22:18 +0200)
CORE-11933 and PR #621.

Since this API is also called from WINSRV when calling the AllocConsole() API,
it can be tested more-or-less easily. The internal helper IntResolveDesktop()
is also tested during process connection to a window station, when such process
first calls a USER32 or GDI32 function.
This is also the functionality tested by the user32:desktop apitest.

- Adjust how IntResolveDesktop() is called.

win32ss/include/ntuser.h
win32ss/user/ntuser/desktop.c
win32ss/user/ntuser/desktop.h
win32ss/user/ntuser/main.c
win32ss/user/winsrv/consrv/frontends/gui/guiterm.c

index 9e4b709..68f37eb 100644 (file)
@@ -2912,11 +2912,11 @@ NtUserRemoveProp(
     ATOM Atom);
 
 HDESK
-APIENTRY
+NTAPI
 NtUserResolveDesktop(
     IN HANDLE ProcessHandle,
     IN PUNICODE_STRING DesktopPath,
-    DWORD dwUnknown,
+    IN BOOL bInherit,
     OUT HWINSTA* phWinSta);
 
 DWORD
index 8f0555a..a90a27c 100644 (file)
@@ -447,139 +447,726 @@ GetSystemVersionString(OUT PWSTR pwszzVersion,
 }
 
 
-NTSTATUS FASTCALL
-IntParseDesktopPath(PEPROCESS Process,
-                    PUNICODE_STRING DesktopPath,
-                    HWINSTA *hWinSta,
-                    HDESK *hDesktop)
+/*
+ * IntResolveDesktop
+ *
+ * The IntResolveDesktop function attempts to retrieve valid handles to
+ * a desktop and a window station suitable for the specified process.
+ * The specified desktop path string is used only as a hint for the resolution.
+ *
+ * - If the process is already assigned to a window station and a desktop,
+ *   handles to these objects are returned directly regardless of the specified
+ *   desktop path string. This is what happens when this function is called for
+ *   a process that has been already started and connected to the Win32 USER.
+ *
+ * - If the process is being connected to the Win32 USER, or is in a state
+ *   where a window station is assigned to it but no desktop yet, the desktop
+ *   path string is used as a hint for the resolution.
+ *   A specified window station (if any, otherwise "WinSta0" is used as default)
+ *   is tested for existence and accessibility. If the checks are OK a handle
+ *   to it is returned. Otherwise we either fail (the window station does not
+ *   exist) or, in case a default window station was used, we attempt to open
+ *   or create a non-interactive Service-0xXXXX-YYYY$ window station. This is
+ *   typically what happens when a non-interactive process is started while
+ *   the WinSta0 window station was used as the default one.
+ *   A specified desktop (if any, otherwise "Default" is used as default)
+ *   is then tested for existence on the opened window station.
+ *
+ * - Rules for the choice of the default window station, when none is specified
+ *   in the desktop path:
+ *
+ *   1. By default, a SYSTEM process connects to a non-interactive window
+ *      station, either the Service-0x0-3e7$ (from the SYSTEM LUID) station,
+ *      or one that has been inherited and that is non-interactive.
+ *      Only when the interactive window station WinSta0 is specified that
+ *      the process can connect to it (e.g. the case of interactive services).
+ *
+ *   2. An interactive process, i.e. a process whose LUID is the same as the
+ *      one assigned to WinSta0 by Winlogon on user logon, connects by default
+ *      to the WinSta0 window station, unless it has inherited from another
+ *      interactive window station (which must be... none other than WinSta0).
+ *
+ *   3. A non-interactive (but not SYSTEM) process connects by default to
+ *      a non-interactive Service-0xXXXX-YYYY$ window station (whose name
+ *      is derived from the process' LUID), or to another non-interactive
+ *      window station that has been inherited.
+ *      Otherwise it may be able connect to the interactive WinSta0 only if
+ *      it has explicit access rights to it.
+ *
+ * Parameters
+ *    Process
+ *       The user process object.
+ *
+ *    DesktopPath
+ *       The desktop path string used as a hint for desktop resolution.
+ *
+ *    bInherit
+ *       Whether or not the returned handles are inheritable.
+ *
+ *    phWinSta
+ *       Pointer to a window station handle.
+ *
+ *    phDesktop
+ *       Pointer to a desktop handle.
+ *
+ * Return Value
+ *    Status code.
+ */
+
+NTSTATUS
+FASTCALL
+IntResolveDesktop(
+    IN PEPROCESS Process,
+    IN PUNICODE_STRING DesktopPath,
+    IN BOOL bInherit,
+    OUT HWINSTA* phWinSta,
+    OUT HDESK* phDesktop)
 {
-    OBJECT_ATTRIBUTES ObjectAttributes;
-    UNICODE_STRING ObjectName;
     NTSTATUS Status;
-    WCHAR wstrWinstaFullName[MAX_PATH], *pwstrWinsta = NULL, *pwstrDesktop = NULL;
-
-    ASSERT(hWinSta);
-    ASSERT(hDesktop);
+    HWINSTA hWinSta = NULL, hWinStaDup = NULL;
+    HDESK hDesktop = NULL, hDesktopDup = NULL;
+    PPROCESSINFO ppi;
+    HANDLE hProcess = NULL;
+    LUID ProcessLuid;
+    USHORT StrSize;
+    SIZE_T MemSize;
+    POBJECT_ATTRIBUTES ObjectAttributes = NULL;
+    PUNICODE_STRING ObjectName;
+    UNICODE_STRING WinStaName, DesktopName;
+    const UNICODE_STRING WinSta0Name = RTL_CONSTANT_STRING(L"WinSta0");
+    PWINSTATION_OBJECT WinStaObject;
+    HWINSTA hTempWinSta = NULL;
+    BOOLEAN bUseDefaultWinSta = FALSE;
+    BOOLEAN bInteractive = FALSE;
+    BOOLEAN bAccessAllowed = FALSE;
+
+    ASSERT(phWinSta);
+    ASSERT(phDesktop);
     ASSERT(DesktopPath);
 
-    *hWinSta = NULL;
-    *hDesktop = NULL;
+    *phWinSta  = NULL;
+    *phDesktop = NULL;
+
+    ppi = PsGetProcessWin32Process(Process);
+    /* ppi is typically NULL for console applications that connect to Win32 USER */
+    if (!ppi) TRACE("IntResolveDesktop: ppi is NULL!\n");
+
+    if (ppi && ppi->hwinsta != NULL && ppi->hdeskStartup != NULL)
+    {
+        /*
+         * If this process is the current one, just return the cached handles.
+         * Otherwise, open the window station and desktop objects.
+         */
+        if (Process == PsGetCurrentProcess())
+        {
+            hWinSta  = ppi->hwinsta;
+            hDesktop = ppi->hdeskStartup;
+        }
+        else
+        {
+            Status = ObOpenObjectByPointer(ppi->prpwinsta,
+                                           0,
+                                           NULL,
+                                           MAXIMUM_ALLOWED,
+                                           ExWindowStationObjectType,
+                                           UserMode,
+                                           (PHANDLE)&hWinSta);
+            if (!NT_SUCCESS(Status))
+            {
+                ERR("IntResolveDesktop: Could not reference window station 0x%p\n", ppi->prpwinsta);
+                SetLastNtError(Status);
+                return Status;
+            }
+
+            Status = ObOpenObjectByPointer(ppi->rpdeskStartup,
+                                           0,
+                                           NULL,
+                                           MAXIMUM_ALLOWED,
+                                           ExDesktopObjectType,
+                                           UserMode,
+                                           (PHANDLE)&hDesktop);
+            if (!NT_SUCCESS(Status))
+            {
+                ERR("IntResolveDesktop: Could not reference desktop 0x%p\n", ppi->rpdeskStartup);
+                ObCloseHandle(hWinSta, UserMode);
+                SetLastNtError(Status);
+                return Status;
+            }
+        }
+
+        *phWinSta  = hWinSta;
+        *phDesktop = hDesktop;
+        return STATUS_SUCCESS;
+    }
+
+    /* We will by default use the default window station and desktop */
+    RtlInitEmptyUnicodeString(&WinStaName, NULL, 0);
+    RtlInitEmptyUnicodeString(&DesktopName, NULL, 0);
 
+    /*
+     * Parse the desktop path string which can be of the form "WinSta\Desktop"
+     * or just "Desktop". In the latter case we use the default window station
+     * on which the process is attached to (or if none, "WinSta0").
+     */
     if (DesktopPath->Buffer != NULL && DesktopPath->Length > sizeof(WCHAR))
+    {
+        DesktopName = *DesktopPath;
+
+        /* Find the separator */
+        while (DesktopName.Length > 0 && *DesktopName.Buffer &&
+               *DesktopName.Buffer != OBJ_NAME_PATH_SEPARATOR)
+        {
+            DesktopName.Buffer++;
+            DesktopName.Length -= sizeof(WCHAR);
+            DesktopName.MaximumLength -= sizeof(WCHAR);
+        }
+        if (DesktopName.Length > 0)
+        {
+            RtlInitEmptyUnicodeString(&WinStaName, DesktopPath->Buffer,
+                                      DesktopPath->Length - DesktopName.Length);
+            // (USHORT)((ULONG_PTR)DesktopName.Buffer - (ULONG_PTR)DesktopPath->Buffer);
+            WinStaName.Length = WinStaName.MaximumLength;
+
+            /* Skip the separator */
+            DesktopName.Buffer++;
+            DesktopName.Length -= sizeof(WCHAR);
+            DesktopName.MaximumLength -= sizeof(WCHAR);
+        }
+        else
+        {
+            RtlInitEmptyUnicodeString(&WinStaName, NULL, 0);
+            DesktopName = *DesktopPath;
+        }
+    }
+
+    TRACE("IntResolveDesktop: WinStaName:'%wZ' ; DesktopName:'%wZ'\n", &WinStaName, &DesktopName);
+
+    /* Retrieve the process LUID */
+    Status = GetProcessLuid(NULL, Process, &ProcessLuid);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("IntResolveDesktop: Failed to retrieve the process LUID, Status 0x%08lx\n", Status);
+        SetLastNtError(Status);
+        return Status;
+    }
+
+    /*
+     * If this process is not the current one, obtain a temporary handle
+     * to it so that we can perform handles duplication later.
+     */
+    if (Process != PsGetCurrentProcess())
+    {
+        Status = ObOpenObjectByPointer(Process,
+                                       OBJ_KERNEL_HANDLE,
+                                       NULL,
+                                       0,
+                                       *PsProcessType,
+                                       KernelMode,
+                                       &hProcess);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("IntResolveDesktop: Failed to obtain a handle to process 0x%p, Status 0x%08lx\n", Process, Status);
+            SetLastNtError(Status);
+            return Status;
+        }
+        ASSERT(hProcess);
+    }
+
+    /*
+     * If no window station has been specified, search the process handle table
+     * for inherited window station handles, otherwise use a default one.
+     */
+    if (WinStaName.Buffer == NULL)
     {
         /*
-         * Parse the desktop path string which can be in the form "WinSta\Desktop"
-         * or just "Desktop". In latter case WinSta0 will be used.
+         * We want to find a suitable default window station.
+         * For applications that can be interactive, i.e. that have allowed
+         * access to the single interactive window station on the system,
+         * the default window station is 'WinSta0'.
+         * For applications that cannot be interactive, i.e. that do not have
+         * access to 'WinSta0' (e.g. non-interactive services), the default
+         * window station is 'Service-0xXXXX-YYYY$' (created if needed).
+         * Precedence will however be taken by any inherited window station
+         * that possesses the required interactivity property.
          */
+        bUseDefaultWinSta = TRUE;
+
+        /*
+         * Use the default 'WinSta0' window station. Whether we should
+         * use 'Service-0xXXXX-YYYY$' instead will be determined later.
+         */
+        // RtlInitUnicodeString(&WinStaName, L"WinSta0");
+        WinStaName = WinSta0Name;
+
+        if (ObFindHandleForObject(Process,
+                                  NULL,
+                                  ExWindowStationObjectType,
+                                  NULL,
+                                  (PHANDLE)&hWinSta))
+        {
+            TRACE("IntResolveDesktop: Inherited window station is: 0x%p\n", hWinSta);
+        }
+    }
+
+    /*
+     * If no desktop has been specified, search the process handle table
+     * for inherited desktop handles, otherwise use the Default desktop.
+     * Note that the inherited desktop that we may use, may not belong
+     * to the window station we will connect to.
+     */
+    if (DesktopName.Buffer == NULL)
+    {
+        /* Use a default desktop name */
+        RtlInitUnicodeString(&DesktopName, L"Default");
+
+        if (ObFindHandleForObject(Process,
+                                  NULL,
+                                  ExDesktopObjectType,
+                                  NULL,
+                                  (PHANDLE)&hDesktop))
+        {
+            TRACE("IntResolveDesktop: Inherited desktop is: 0x%p\n", hDesktop);
+        }
+    }
+
+
+    /*
+     * We are going to open either a window station or a desktop.
+     * Even if this operation is done from kernel-mode, we should
+     * "emulate" an opening from user-mode (i.e. using an ObjectAttributes
+     * allocated in user-mode, with AccessMode == UserMode) for the
+     * Object Manager to perform proper access validation to the
+     * window station or desktop.
+     */
+
+    /*
+     * Estimate the maximum size needed for the window station name
+     * and desktop name to be given to ObjectAttributes->ObjectName.
+     */
+    StrSize = 0;
+
+    /* Window station name */
+    MemSize = _scwprintf(L"Service-0x%x-%x$", MAXULONG, MAXULONG) * sizeof(WCHAR);
+    MemSize = gustrWindowStationsDir.Length + sizeof(OBJ_NAME_PATH_SEPARATOR)
+              + max(WinStaName.Length, MemSize) + sizeof(UNICODE_NULL);
+    if (MemSize > MAXUSHORT)
+    {
+        ERR("IntResolveDesktop: Window station name length is too long.\n");
+        Status = STATUS_NAME_TOO_LONG;
+        goto Quit;
+    }
+    StrSize = max(StrSize, (USHORT)MemSize);
+
+    /* Desktop name */
+    MemSize = max(DesktopName.Length + sizeof(UNICODE_NULL), sizeof(L"Default"));
+    StrSize = max(StrSize, (USHORT)MemSize);
+
+    /* Size for the OBJECT_ATTRIBUTES */
+    MemSize = ALIGN_UP(sizeof(OBJECT_ATTRIBUTES), sizeof(PVOID));
+
+    /* Add the string size */
+    MemSize += ALIGN_UP(sizeof(UNICODE_STRING), sizeof(PVOID));
+    MemSize += StrSize;
+
+    /* Allocate the memory in user-mode */
+    Status = ZwAllocateVirtualMemory(ZwCurrentProcess(),
+                                     (PVOID*)&ObjectAttributes,
+                                     0,
+                                     &MemSize,
+                                     MEM_COMMIT,
+                                     PAGE_READWRITE);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("ZwAllocateVirtualMemory() failed, Status 0x%08lx\n", Status);
+        goto Quit;
+    }
+
+    ObjectName = (PUNICODE_STRING)((ULONG_PTR)ObjectAttributes +
+                     ALIGN_UP(sizeof(OBJECT_ATTRIBUTES), sizeof(PVOID)));
+
+    RtlInitEmptyUnicodeString(ObjectName,
+                              (PWCHAR)((ULONG_PTR)ObjectName +
+                                  ALIGN_UP(sizeof(UNICODE_STRING), sizeof(PVOID))),
+                              StrSize);
+
 
-        pwstrDesktop = wcschr(DesktopPath->Buffer, L'\\');
-        if (pwstrDesktop != NULL)
+    /* If we got an inherited window station handle, duplicate and use it */
+    if (hWinSta)
+    {
+        ASSERT(bUseDefaultWinSta);
+
+        /* Duplicate the handle if it belongs to another process than the current one */
+        if (Process != PsGetCurrentProcess())
         {
-            *pwstrDesktop = 0;
-            pwstrDesktop++;
-            pwstrWinsta = DesktopPath->Buffer;
+            ASSERT(hProcess);
+            Status = ZwDuplicateObject(hProcess,
+                                       hWinSta,
+                                       ZwCurrentProcess(),
+                                       (PHANDLE)&hWinStaDup,
+                                       0,
+                                       0,
+                                       DUPLICATE_SAME_ACCESS);
+            if (!NT_SUCCESS(Status))
+            {
+                ERR("IntResolveDesktop: Failed to duplicate the window station handle, Status 0x%08lx\n", Status);
+                /* We will use a default window station */
+                hWinSta = NULL;
+            }
+            else
+            {
+                hWinSta = hWinStaDup;
+            }
+        }
+    }
+
+    /*
+     * If we have an inherited window station, check whether
+     * it is interactive and remember that for later.
+     */
+    if (hWinSta)
+    {
+        ASSERT(bUseDefaultWinSta);
+
+        /* Reference the inherited window station */
+        Status = ObReferenceObjectByHandle(hWinSta,
+                                           0,
+                                           ExWindowStationObjectType,
+                                           KernelMode,
+                                           (PVOID*)&WinStaObject,
+                                           NULL);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("Failed to reference the inherited window station, Status 0x%08lx\n", Status);
+            /* We will use a default window station */
+            if (hWinStaDup)
+            {
+                ASSERT(hWinSta == hWinStaDup);
+                ObCloseHandle(hWinStaDup, UserMode);
+                hWinStaDup = NULL;
+            }
+            hWinSta = NULL;
         }
         else
         {
-            pwstrDesktop = DesktopPath->Buffer;
-            pwstrWinsta = NULL;
+            ERR("Process LUID is: 0x%x-%x, inherited window station LUID is: 0x%x-%x\n",
+                ProcessLuid.HighPart, ProcessLuid.LowPart,
+                WinStaObject->luidUser.HighPart, WinStaObject->luidUser.LowPart);
+
+            /* Check whether this window station is interactive, and remember it for later */
+            bInteractive = !(WinStaObject->Flags & WSS_NOIO);
+
+            /* Dereference the window station */
+            ObDereferenceObject(WinStaObject);
         }
+    }
 
-        TRACE("IntParseDesktopPath pwstrWinsta:%S pwstrDesktop:%S\n", pwstrWinsta, pwstrDesktop);
+    /* Build a valid window station name */
+    Status = RtlStringCbPrintfW(ObjectName->Buffer,
+                                ObjectName->MaximumLength,
+                                L"%wZ\\%wZ",
+                                &gustrWindowStationsDir,
+                                &WinStaName);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("Impossible to build a valid window station name, Status 0x%08lx\n", Status);
+        goto Quit;
     }
+    ObjectName->Length = (USHORT)(wcslen(ObjectName->Buffer) * sizeof(WCHAR));
 
-#if 0
-    /* Search the process handle table for (inherited) window station
-       handles, use a more appropriate one than WinSta0 if possible. */
-    if (!ObFindHandleForObject(Process,
-                               NULL,
-                               ExWindowStationObjectType,
+    TRACE("Parsed initial window station: '%wZ'\n", ObjectName);
+
+    /* Try to open the window station */
+    InitializeObjectAttributes(ObjectAttributes,
+                               ObjectName,
+                               OBJ_CASE_INSENSITIVE,
                                NULL,
-                               (PHANDLE)hWinSta))
-#endif
+                               NULL);
+    if (bInherit)
+        ObjectAttributes->Attributes |= OBJ_INHERIT;
+
+    Status = ObOpenObjectByName(ObjectAttributes,
+                                ExWindowStationObjectType,
+                                UserMode,
+                                NULL,
+                                WINSTA_ACCESS_ALL,
+                                NULL,
+                                (PHANDLE)&hTempWinSta);
+    if (!NT_SUCCESS(Status))
     {
-        /* We had no luck searching for opened handles, use WinSta0 now */
-        if (!pwstrWinsta)
-            pwstrWinsta = L"WinSta0";
+        ERR("Failed to open the window station '%wZ', Status 0x%08lx\n", ObjectName, Status);
     }
+    else
+    {
+        //
+        // FIXME TODO: Perform a window station access check!!
+        // If we fail AND bUseDefaultWinSta == FALSE we just quit.
+        //
 
-#if 0
-    /* Search the process handle table for (inherited) desktop
-       handles, use a more appropriate one than Default if possible. */
-    if (!ObFindHandleForObject(Process,
-                               NULL,
-                               ExDesktopObjectType,
-                               NULL,
-                               (PHANDLE)hDesktop))
-#endif
+        /*
+         * Check whether we are opening the (single) interactive
+         * window station, and if so, perform an access check.
+         */
+        /* Check whether we are allowed to perform interactions */
+        if (RtlEqualUnicodeString(&WinStaName, &WinSta0Name, TRUE))
+        {
+            LUID SystemLuid = SYSTEM_LUID;
+
+            /* Interactive window station: check for user LUID */
+            WinStaObject = InputWindowStation;
+
+            Status = STATUS_ACCESS_DENIED;
+
+            // TODO: Check also that we compare wrt. window station WinSta0
+            // which is the only one that can be interactive on the system.
+            if (((!bUseDefaultWinSta || bInherit) && RtlEqualLuid(&ProcessLuid, &SystemLuid)) ||
+                 RtlEqualLuid(&ProcessLuid, &WinStaObject->luidUser))
+            {
+                /* We are interactive on this window station */
+                bAccessAllowed = TRUE;
+                Status = STATUS_SUCCESS;
+            }
+        }
+        else
+        {
+            /* Non-interactive window station: we have access since we were able to open it */
+            bAccessAllowed = TRUE;
+            Status = STATUS_SUCCESS;
+        }
+    }
+
+    /* If we failed, bail out if we were not trying to open the default window station */
+    if (!NT_SUCCESS(Status) && !bUseDefaultWinSta) // if (!bAccessAllowed)
+        goto Quit;
+
+    if (/* bAccessAllowed && */ bInteractive || !bAccessAllowed)
+    {
+        /*
+         * Close WinSta0 if the inherited window station is interactive so that
+         * we can use it, or we do not have access to the interactive WinSta0.
+         */
+        ObCloseHandle(hTempWinSta, UserMode);
+        hTempWinSta = NULL;
+    }
+    if (bInteractive == bAccessAllowed)
+    {
+        /* Keep using the inherited window station */
+        NOTHING;
+    }
+    else // if (bInteractive != bAccessAllowed)
     {
-        /* We had no luck searching for opened handles, use Desktop now */
-        if (!pwstrDesktop)
-            pwstrDesktop = L"Default";
+        /*
+         * Close the inherited window station, we will either keep using
+         * the interactive WinSta0, or use Service-0xXXXX-YYYY$.
+         */
+        if (hWinStaDup)
+        {
+            ASSERT(hWinSta == hWinStaDup);
+            ObCloseHandle(hWinStaDup, UserMode);
+            hWinStaDup = NULL;
+        }
+        hWinSta = hTempWinSta; // hTempWinSta is NULL in case bAccessAllowed == FALSE
     }
 
-    if (*hWinSta == NULL)
+    if (bUseDefaultWinSta)
     {
-        swprintf(wstrWinstaFullName, L"%wZ\\%ws", &gustrWindowStationsDir, pwstrWinsta);
-        RtlInitUnicodeString( &ObjectName, wstrWinstaFullName);
+        if (hWinSta == NULL && !bInteractive)
+        {
+            /* Build a valid window station name from the LUID */
+            Status = RtlStringCbPrintfW(ObjectName->Buffer,
+                                        ObjectName->MaximumLength,
+                                        L"%wZ\\Service-0x%x-%x$",
+                                        &gustrWindowStationsDir,
+                                        ProcessLuid.HighPart,
+                                        ProcessLuid.LowPart);
+            if (!NT_SUCCESS(Status))
+            {
+                ERR("Impossible to build a valid window station name, Status 0x%08lx\n", Status);
+                goto Quit;
+            }
+            ObjectName->Length = (USHORT)(wcslen(ObjectName->Buffer) * sizeof(WCHAR));
+
+            /*
+             * Create or open the non-interactive window station.
+             * NOTE: The non-interactive window station handle is never inheritable.
+             */
+            // FIXME: Set security!
+            InitializeObjectAttributes(ObjectAttributes,
+                                       ObjectName,
+                                       OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
+                                       NULL,
+                                       NULL);
 
-        TRACE("parsed initial winsta: %wZ\n", &ObjectName);
+            Status = IntCreateWindowStation(&hWinSta,
+                                            ObjectAttributes,
+                                            UserMode,
+                                            KernelMode,
+                                            MAXIMUM_ALLOWED,
+                                            0, 0, 0, 0, 0);
+            if (!NT_SUCCESS(Status))
+            {
+                ASSERT(hWinSta == NULL);
+                ERR("Failed to create or open the non-interactive window station '%wZ', Status 0x%08lx\n",
+                    ObjectName, Status);
+                goto Quit;
+            }
 
-        /* Open the window station */
-        InitializeObjectAttributes(&ObjectAttributes,
-                                   &ObjectName,
-                                   OBJ_CASE_INSENSITIVE,
-                                   NULL,
-                                   NULL);
+            //
+            // FIXME: We might not need to always create or open the "Default"
+            // desktop on the Service-0xXXXX-YYYY$ window station; we may need
+            // to use another one....
+            //
+
+            /* Create or open the Default desktop on the window station */
+            Status = RtlStringCbCopyW(ObjectName->Buffer,
+                                      ObjectName->MaximumLength,
+                                      L"Default");
+            if (!NT_SUCCESS(Status))
+            {
+                ERR("Impossible to build a valid desktop name, Status 0x%08lx\n", Status);
+                goto Quit;
+            }
+            ObjectName->Length = (USHORT)(wcslen(ObjectName->Buffer) * sizeof(WCHAR));
+
+            /* NOTE: The non-interactive desktop handle is never inheritable. */
+            // FIXME: Set security!
+            InitializeObjectAttributes(ObjectAttributes,
+                                       ObjectName,
+                                       OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
+                                       hWinSta,
+                                       NULL);
 
-        Status = ObOpenObjectByName(&ObjectAttributes,
-                                    ExWindowStationObjectType,
-                                    KernelMode,
-                                    NULL,
-                                    WINSTA_ACCESS_ALL,
-                                    NULL,
-                                    (HANDLE*)hWinSta);
+            Status = IntCreateDesktop(&hDesktop,
+                                      ObjectAttributes,
+                                      UserMode,
+                                      NULL,
+                                      NULL,
+                                      0,
+                                      MAXIMUM_ALLOWED);
+            if (!NT_SUCCESS(Status))
+            {
+                ASSERT(hDesktop == NULL);
+                ERR("Failed to create or open the desktop '%wZ' on window station 0x%p, Status 0x%08lx\n",
+                    ObjectName, hWinSta, Status);
+            }
 
-        if (!NT_SUCCESS(Status))
+            goto Quit;
+        }
+/*
+        if (hWinSta == NULL)
         {
-            SetLastNtError(Status);
-            ERR("Failed to reference window station %wZ PID: --!\n", &ObjectName );
-            return Status;
+            Status = STATUS_UNSUCCESSFUL;
+            goto Quit;
         }
+*/
     }
 
-    if (*hDesktop == NULL)
+    /*
+     * If we got an inherited desktop handle, duplicate and use it,
+     * otherwise open a new desktop.
+     */
+    if (hDesktop != NULL)
     {
-        RtlInitUnicodeString(&ObjectName, pwstrDesktop);
+        /* Duplicate the handle if it belongs to another process than the current one */
+        if (Process != PsGetCurrentProcess())
+        {
+            ASSERT(hProcess);
+            Status = ZwDuplicateObject(hProcess,
+                                       hDesktop,
+                                       ZwCurrentProcess(),
+                                       (PHANDLE)&hDesktopDup,
+                                       0,
+                                       0,
+                                       DUPLICATE_SAME_ACCESS);
+            if (!NT_SUCCESS(Status))
+            {
+                ERR("IntResolveDesktop: Failed to duplicate the desktop handle, Status 0x%08lx\n", Status);
+                /* We will use a default desktop */
+                hDesktop = NULL;
+            }
+            else
+            {
+                hDesktop = hDesktopDup;
+            }
+        }
+    }
 
-        TRACE("parsed initial desktop: %wZ\n", &ObjectName);
+    if ((hWinSta != NULL) && (hDesktop == NULL))
+    {
+        Status = RtlStringCbCopyNW(ObjectName->Buffer,
+                                   ObjectName->MaximumLength,
+                                   DesktopName.Buffer,
+                                   DesktopName.Length);
+        if (!NT_SUCCESS(Status))
+        {
+            ERR("Impossible to build a valid desktop name, Status 0x%08lx\n", Status);
+            goto Quit;
+        }
+        ObjectName->Length = (USHORT)(wcslen(ObjectName->Buffer) * sizeof(WCHAR));
+
+        TRACE("Parsed initial desktop: '%wZ'\n", ObjectName);
 
         /* Open the desktop object */
-        InitializeObjectAttributes(&ObjectAttributes,
-                                   &ObjectName,
+        InitializeObjectAttributes(ObjectAttributes,
+                                   ObjectName,
                                    OBJ_CASE_INSENSITIVE,
-                                   *hWinSta,
+                                   hWinSta,
                                    NULL);
+        if (bInherit)
+            ObjectAttributes->Attributes |= OBJ_INHERIT;
 
-        Status = ObOpenObjectByName(&ObjectAttributes,
+        Status = ObOpenObjectByName(ObjectAttributes,
                                     ExDesktopObjectType,
-                                    KernelMode,
+                                    UserMode,
                                     NULL,
                                     DESKTOP_ALL_ACCESS,
                                     NULL,
-                                    (HANDLE*)hDesktop);
-
+                                    (PHANDLE)&hDesktop);
         if (!NT_SUCCESS(Status))
         {
-            *hDesktop = NULL;
-            NtClose(*hWinSta);
-            *hWinSta = NULL;
-            SetLastNtError(Status);
-            ERR("Failed to reference desktop %wZ PID: --!\n", &ObjectName);
-            return Status;
+            ERR("Failed to open the desktop '%wZ' on window station 0x%p, Status 0x%08lx\n",
+                ObjectName, hWinSta, Status);
+            goto Quit;
         }
     }
-    return STATUS_SUCCESS;
+
+Quit:
+    /* Release the object attributes */
+    if (ObjectAttributes)
+    {
+        MemSize = 0;
+        ZwFreeVirtualMemory(ZwCurrentProcess(),
+                            (PVOID*)&ObjectAttributes,
+                            &MemSize,
+                            MEM_RELEASE);
+    }
+
+    /* Close the temporary process handle */
+    if (hProcess) // if (Process != PsGetCurrentProcess())
+        ObCloseHandle(hProcess, KernelMode);
+
+    if (NT_SUCCESS(Status))
+    {
+        *phWinSta  = hWinSta;
+        *phDesktop = hDesktop;
+        return STATUS_SUCCESS;
+    }
+    else
+    {
+        ERR("IntResolveDesktop(%wZ) failed, Status 0x%08lx\n", DesktopPath, Status);
+
+        if (hDesktopDup)
+            ObCloseHandle(hDesktopDup, UserMode);
+        if (hWinStaDup)
+            ObCloseHandle(hWinStaDup, UserMode);
+
+        if (hDesktop)
+            ObCloseHandle(hDesktop, UserMode);
+        if (hWinSta)
+            ObCloseHandle(hWinSta, UserMode);
+
+        SetLastNtError(Status);
+        return Status;
+    }
 }
 
 /*
@@ -2115,15 +2702,24 @@ NtUserPaintDesktop(HDC hDC)
 /*
  * NtUserResolveDesktop
  *
- * The NtUserResolveDesktop function retrieves handles to the desktop and
- * the window station specified by the desktop path string.
+ * The NtUserResolveDesktop function attempts to retrieve valid handles to
+ * a desktop and a window station suitable for the specified process.
+ * The specified desktop path string is used only as a hint for the resolution.
+ *
+ * See the description of IntResolveDesktop for more details.
  *
  * Parameters
  *    ProcessHandle
  *       Handle to a user process.
  *
  *    DesktopPath
- *       The desktop path string.
+ *       The desktop path string used as a hint for desktop resolution.
+ *
+ *    bInherit
+ *       Whether or not the returned handles are inheritable.
+ *
+ *    phWinSta
+ *       Pointer to a window station handle.
  *
  * Return Value
  *    Handle to the desktop (direct return value) and
@@ -2138,17 +2734,18 @@ NtUserPaintDesktop(HDC hDC)
  */
 
 HDESK
-APIENTRY
+NTAPI
 NtUserResolveDesktop(
     IN HANDLE ProcessHandle,
     IN PUNICODE_STRING DesktopPath,
-    DWORD dwUnknown,
+    IN BOOL bInherit,
     OUT HWINSTA* phWinSta)
 {
     NTSTATUS Status;
-    PEPROCESS Process = NULL;
+    PEPROCESS Process;
     HWINSTA hWinSta = NULL;
     HDESK hDesktop  = NULL;
+    UNICODE_STRING CapturedDesktopPath;
 
     /* Allow only the Console Server to perform this operation (via CSRSS) */
     if (PsGetCurrentProcess() != gpepCSRSS)
@@ -2161,44 +2758,63 @@ NtUserResolveDesktop(
                                        UserMode,
                                        (PVOID*)&Process,
                                        NULL);
-    if (!NT_SUCCESS(Status)) return NULL;
+    if (!NT_SUCCESS(Status))
+        return NULL;
 
     // UserEnterShared();
 
     _SEH2_TRY
     {
-        UNICODE_STRING CapturedDesktopPath;
+        /* Probe the handle pointer */
+        // ProbeForWriteHandle
+        ProbeForWrite(phWinSta, sizeof(HWINSTA), sizeof(HWINSTA));
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status = _SEH2_GetExceptionCode();
+        _SEH2_YIELD(goto Quit);
+    }
+    _SEH2_END;
 
-        /* Capture the user desktop path string */
-        Status = IntSafeCopyUnicodeStringTerminateNULL(&CapturedDesktopPath,
-                                                       DesktopPath);
-        if (!NT_SUCCESS(Status)) _SEH2_YIELD(goto Quit);
+    /* Capture the user desktop path string */
+    Status = ProbeAndCaptureUnicodeString(&CapturedDesktopPath,
+                                          UserMode,
+                                          DesktopPath);
+    if (!NT_SUCCESS(Status))
+        goto Quit;
 
-        /* Call the internal function */
-        Status = IntParseDesktopPath(Process,
-                                     &CapturedDesktopPath,
-                                     &hWinSta,
-                                     &hDesktop);
-        if (!NT_SUCCESS(Status))
-        {
-            ERR("IntParseDesktopPath failed, Status = 0x%08lx\n", Status);
-            hWinSta  = NULL;
-            hDesktop = NULL;
-        }
+    /* Call the internal function */
+    Status = IntResolveDesktop(Process,
+                               &CapturedDesktopPath,
+                               bInherit,
+                               &hWinSta,
+                               &hDesktop);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("IntResolveDesktop failed, Status 0x%08lx\n", Status);
+        hWinSta  = NULL;
+        hDesktop = NULL;
+    }
 
+    _SEH2_TRY
+    {
         /* Return the window station handle */
         *phWinSta = hWinSta;
-
-        /* Free the captured string */
-        if (CapturedDesktopPath.Buffer)
-            ExFreePoolWithTag(CapturedDesktopPath.Buffer, TAG_STRING);
     }
     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
         Status = _SEH2_GetExceptionCode();
+
+        /* We failed, close the opened desktop and window station */
+        if (hDesktop) ObCloseHandle(hDesktop, UserMode);
+        hDesktop = NULL;
+        if (hWinSta) ObCloseHandle(hWinSta, UserMode);
     }
     _SEH2_END;
 
+    /* Free the captured string */
+    ReleaseCapturedUnicodeString(&CapturedDesktopPath, UserMode);
+
 Quit:
     // UserLeave();
 
index 7156f22..0e1950f 100644 (file)
@@ -161,6 +161,15 @@ IntHideDesktop(PDESKTOP Desktop);
 BOOL IntSetThreadDesktop(IN HDESK hDesktop,
                          IN BOOL FreeOnFailure);
 
+NTSTATUS
+FASTCALL
+IntResolveDesktop(
+    IN PEPROCESS Process,
+    IN PUNICODE_STRING DesktopPath,
+    IN BOOL bInherit,
+    OUT HWINSTA* phWinSta,
+    OUT HDESK* phDesktop);
+
 NTSTATUS FASTCALL
 IntValidateDesktopHandle(
    HDESK Desktop,
@@ -179,12 +188,6 @@ IntCreateDesktop(
     IN DWORD dwFlags,
     IN ACCESS_MASK dwDesiredAccess);
 
-NTSTATUS FASTCALL
-IntParseDesktopPath(PEPROCESS Process,
-                    PUNICODE_STRING DesktopPath,
-                    HWINSTA *hWinSta,
-                    HDESK *hDesktop);
-
 VOID APIENTRY UserRedrawDesktop(VOID);
 BOOL IntRegisterShellHookWindow(HWND hWnd);
 BOOL IntDeRegisterShellHookWindow(HWND hWnd);
index c10e482..2fc1b69 100644 (file)
@@ -532,12 +532,12 @@ InitThreadCallback(PETHREAD Thread)
 
     ptiCurrent->TIF_flags &= ~TIF_INCLEANUP;
 
+    // FIXME: Flag SYSTEM threads with... TIF_SYSTEMTHREAD !!
+
     /* CSRSS threads have some special features */
     if (Process == gpepCSRSS)
         ptiCurrent->TIF_flags = TIF_CSRSSTHREAD | TIF_DONTATTACHQUEUE;
 
-    // FIXME: Flag SYSTEM threads with... TIF_SYSTEMTHREAD !!
-
     ptiCurrent->pcti = &ptiCurrent->cti;
 
     /* Initialize the CLIENTINFO */
@@ -570,9 +570,16 @@ InitThreadCallback(PETHREAD Thread)
        }
     }
 
-    /* Assign a default window station and desktop to the process */
-    /* Do not try to open a desktop or window station before winlogon initializes */
-    if (ptiCurrent->ppi->hdeskStartup == NULL && gpidLogon != 0)
+    /*
+     * Assign a default window station and desktop to the process.
+     * Do not try to open a desktop or window station before the very first
+     * (interactive) window station has been created by Winlogon.
+     */
+    // if (ptiCurrent->ppi->hdeskStartup == NULL && InputWindowStation != NULL)
+    /* Last things to do only if we are not a SYSTEM or CSRSS thread */
+    if (!(ptiCurrent->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD)) &&
+        /**/ptiCurrent->ppi->hdeskStartup == NULL &&/**/
+        InputWindowStation != NULL)
     {
         HWINSTA hWinSta = NULL;
         HDESK hDesk = NULL;
@@ -580,8 +587,8 @@ InitThreadCallback(PETHREAD Thread)
         PDESKTOP pdesk;
 
         /*
-         * inherit the thread desktop and process window station (if not yet inherited) from the process startup
-         * info structure. See documentation of CreateProcess()
+         * Inherit the thread desktop and process window station (if not yet inherited)
+         * from the process startup info structure. See documentation of CreateProcess().
          */
 
         Status = STATUS_UNSUCCESSFUL;
@@ -594,17 +601,18 @@ InitThreadCallback(PETHREAD Thread)
             RtlInitUnicodeString(&DesktopPath, NULL);
         }
 
-        Status = IntParseDesktopPath(Process,
-                                     &DesktopPath,
-                                     &hWinSta,
-                                     &hDesk);
+        Status = IntResolveDesktop(Process,
+                                   &DesktopPath,
+                                   FALSE,
+                                   &hWinSta,
+                                   &hDesk);
 
         if (DesktopPath.Buffer)
             ExFreePoolWithTag(DesktopPath.Buffer, TAG_STRING);
 
         if (!NT_SUCCESS(Status))
         {
-            ERR_CH(UserThread, "Failed to assign default dekstop and winsta to process\n");
+            ERR_CH(UserThread, "Failed to assign default desktop and winsta to process\n");
             goto error;
         }
 
@@ -615,7 +623,7 @@ InitThreadCallback(PETHREAD Thread)
             goto error;
         }
 
-        /* Validate the new desktop. */
+        /* Validate the new desktop */
         Status = IntValidateDesktopHandle(hDesk, UserMode, 0, &pdesk);
         if (!NT_SUCCESS(Status))
         {
@@ -624,6 +632,8 @@ InitThreadCallback(PETHREAD Thread)
         }
 
         /* Store the parsed desktop as the initial desktop */
+        ASSERT(ptiCurrent->ppi->hdeskStartup == NULL);
+        ASSERT(Process->UniqueProcessId != gpidLogon);
         ptiCurrent->ppi->hdeskStartup = hDesk;
         ptiCurrent->ppi->rpdeskStartup = pdesk;
     }
index a3ebb03..258b1e2 100644 (file)
@@ -324,7 +324,7 @@ GuiInit(IN PCONSOLE_INIT_INFO ConsoleInitInfo,
 
     hDesk = NtUserResolveDesktop(ConsoleLeaderProcessHandle,
                                  &DesktopPath,
-                                 0,
+                                 FALSE,
                                  &hWinSta);
     DPRINT("NtUserResolveDesktop(DesktopPath = '%wZ') returned hDesk = 0x%p; hWinSta = 0x%p\n",
            &DesktopPath, hDesk, hWinSta);