https://git.reactos.org/?p=reactos.git;a=commitdiff;h=c697f191cf72ce02d987a…
commit c697f191cf72ce02d987aeac7cfff87174461622
Author: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
AuthorDate: Sun Jul 22 20:38:26 2018 +0200
Commit: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
CommitDate: Sun Aug 19 22:18:37 2018 +0200
[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 | 838 ++++++++++++++++++---
win32ss/user/ntuser/desktop.h | 15 +-
win32ss/user/ntuser/main.c | 36 +-
win32ss/user/winsrv/consrv/frontends/gui/guiterm.c | 2 +-
5 files changed, 762 insertions(+), 133 deletions(-)
diff --git a/win32ss/include/ntuser.h b/win32ss/include/ntuser.h
index 9e4b70938b..68f37ebff8 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 8f0555a746..a90a27caa1 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)
+/*
+ * 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();
diff --git a/win32ss/user/ntuser/desktop.h b/win32ss/user/ntuser/desktop.h
index 7156f22bf7..0e1950ff64 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 c10e48230e..2fc1b69082 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 a3ebb0390c..258b1e28e6 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);