From c697f191cf72ce02d987aeac7cfff87174461622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Sun, 22 Jul 2018 20:38:26 +0200 Subject: [PATCH] [WIN32K:NTUSER] Make NtUserResolveDesktop() and IntResolveDesktop() work in a more Win2k3-compatible manner. 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 | 4 +- win32ss/user/ntuser/desktop.c | 858 +++++++++++++++--- win32ss/user/ntuser/desktop.h | 15 +- win32ss/user/ntuser/main.c | 36 +- .../winsrv/consrv/frontends/gui/guiterm.c | 2 +- 5 files changed, 772 insertions(+), 143 deletions(-) diff --git a/win32ss/include/ntuser.h b/win32ss/include/ntuser.h index 9e4b70938be..68f37ebff8f 100644 --- a/win32ss/include/ntuser.h +++ b/win32ss/include/ntuser.h @@ -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 diff --git a/win32ss/user/ntuser/desktop.c b/win32ss/user/ntuser/desktop.c index 8f0555a746a..a90a27caa1c 100644 --- a/win32ss/user/ntuser/desktop.c +++ b/win32ss/user/ntuser/desktop.c @@ -447,139 +447,726 @@ GetSystemVersionString(OUT PWSTR pwszzVersion, } -NTSTATUS FASTCALL -IntParseDesktopPath(PEPROCESS Process, - PUNICODE_STRING DesktopPath, - HWINSTA *hWinSta, - HDESK *hDesktop) -{ - OBJECT_ATTRIBUTES ObjectAttributes; - UNICODE_STRING ObjectName; - NTSTATUS Status; - WCHAR wstrWinstaFullName[MAX_PATH], *pwstrWinsta = NULL, *pwstrDesktop = NULL; +/* + * 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. + */ - ASSERT(hWinSta); - ASSERT(hDesktop); +NTSTATUS +FASTCALL +IntResolveDesktop( + IN PEPROCESS Process, + IN PUNICODE_STRING DesktopPath, + IN BOOL bInherit, + OUT HWINSTA* phWinSta, + OUT HDESK* phDesktop) +{ + NTSTATUS Status; + 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; - if (DesktopPath->Buffer != NULL && DesktopPath->Length > sizeof(WCHAR)) + 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) { /* - * Parse the desktop path string which can be in the form "WinSta\Desktop" - * or just "Desktop". In latter case WinSta0 will be used. + * If this process is the current one, just return the cached handles. + * Otherwise, open the window station and desktop objects. */ - - pwstrDesktop = wcschr(DesktopPath->Buffer, L'\\'); - if (pwstrDesktop != NULL) + if (Process == PsGetCurrentProcess()) { - *pwstrDesktop = 0; - pwstrDesktop++; - pwstrWinsta = DesktopPath->Buffer; + hWinSta = ppi->hwinsta; + hDesktop = ppi->hdeskStartup; } else { - pwstrDesktop = DesktopPath->Buffer; - pwstrWinsta = NULL; + 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; + } } - TRACE("IntParseDesktopPath pwstrWinsta:%S pwstrDesktop:%S\n", pwstrWinsta, pwstrDesktop); + *phWinSta = hWinSta; + *phDesktop = hDesktop; + return STATUS_SUCCESS; } -#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, - NULL, - (PHANDLE)hWinSta)) -#endif + /* 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)) { - /* We had no luck searching for opened handles, use WinSta0 now */ - if (!pwstrWinsta) - pwstrWinsta = L"WinSta0"; + 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; + } } -#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 + TRACE("IntResolveDesktop: WinStaName:'%wZ' ; DesktopName:'%wZ'\n", &WinStaName, &DesktopName); + + /* Retrieve the process LUID */ + Status = GetProcessLuid(NULL, Process, &ProcessLuid); + if (!NT_SUCCESS(Status)) { - /* We had no luck searching for opened handles, use Desktop now */ - if (!pwstrDesktop) - pwstrDesktop = L"Default"; + ERR("IntResolveDesktop: Failed to retrieve the process LUID, Status 0x%08lx\n", Status); + SetLastNtError(Status); + return Status; } - if (*hWinSta == NULL) + /* + * 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()) { - swprintf(wstrWinstaFullName, L"%wZ\\%ws", &gustrWindowStationsDir, pwstrWinsta); - RtlInitUnicodeString( &ObjectName, wstrWinstaFullName); - - TRACE("parsed initial winsta: %wZ\n", &ObjectName); - - /* Open the window station */ - InitializeObjectAttributes(&ObjectAttributes, - &ObjectName, - OBJ_CASE_INSENSITIVE, - NULL, - NULL); - - Status = ObOpenObjectByName(&ObjectAttributes, - ExWindowStationObjectType, - KernelMode, - NULL, - WINSTA_ACCESS_ALL, - NULL, - (HANDLE*)hWinSta); - + 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); - ERR("Failed to reference window station %wZ PID: --!\n", &ObjectName ); 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) + { + /* + * 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 (*hDesktop == NULL) + /* + * 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) { - RtlInitUnicodeString(&ObjectName, pwstrDesktop); + /* Use a default desktop name */ + RtlInitUnicodeString(&DesktopName, L"Default"); - TRACE("parsed initial desktop: %wZ\n", &ObjectName); + 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); + + + /* 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()) + { + 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 + { + 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); + } + } + + /* 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)); + + TRACE("Parsed initial window station: '%wZ'\n", ObjectName); + + /* Try to open the window station */ + InitializeObjectAttributes(ObjectAttributes, + ObjectName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + if (bInherit) + ObjectAttributes->Attributes |= OBJ_INHERIT; + + Status = ObOpenObjectByName(ObjectAttributes, + ExWindowStationObjectType, + UserMode, + NULL, + WINSTA_ACCESS_ALL, + NULL, + (PHANDLE)&hTempWinSta); + if (!NT_SUCCESS(Status)) + { + 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. + // + + /* + * 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) + { + /* + * 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 (bUseDefaultWinSta) + { + 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); + + 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; + } + + // + // 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 = 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); + } + + goto Quit; + } +/* + if (hWinSta == NULL) + { + Status = STATUS_UNSUCCESSFUL; + goto Quit; + } +*/ + } + + /* + * If we got an inherited desktop handle, duplicate and use it, + * otherwise open a new desktop. + */ + if (hDesktop != NULL) + { + /* 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; + } + } + } + + 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; - - /* Capture the user desktop path string */ - Status = IntSafeCopyUnicodeStringTerminateNULL(&CapturedDesktopPath, - DesktopPath); - if (!NT_SUCCESS(Status)) _SEH2_YIELD(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; - } - - /* Return the window station handle */ - *phWinSta = hWinSta; - - /* Free the captured string */ - if (CapturedDesktopPath.Buffer) - ExFreePoolWithTag(CapturedDesktopPath.Buffer, TAG_STRING); + /* 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 = ProbeAndCaptureUnicodeString(&CapturedDesktopPath, + UserMode, + DesktopPath); + if (!NT_SUCCESS(Status)) + goto Quit; + + /* 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; + } + _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(); diff --git a/win32ss/user/ntuser/desktop.h b/win32ss/user/ntuser/desktop.h index 7156f22bf7a..0e1950ff645 100644 --- a/win32ss/user/ntuser/desktop.h +++ b/win32ss/user/ntuser/desktop.h @@ -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); diff --git a/win32ss/user/ntuser/main.c b/win32ss/user/ntuser/main.c index c10e48230eb..2fc1b69082d 100644 --- a/win32ss/user/ntuser/main.c +++ b/win32ss/user/ntuser/main.c @@ -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; } diff --git a/win32ss/user/winsrv/consrv/frontends/gui/guiterm.c b/win32ss/user/winsrv/consrv/frontends/gui/guiterm.c index a3ebb0390c7..258b1e28e66 100644 --- a/win32ss/user/winsrv/consrv/frontends/gui/guiterm.c +++ b/win32ss/user/winsrv/consrv/frontends/gui/guiterm.c @@ -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);