[WIN32K:NTUSER] Implement security infrastructure for NTUSER component

Implement a base security infrastructure with code that sets up a security descriptor for the service that we're going to connect through it. Such service is based upon a desktop and a window station.

=== DOCUMENTATION REMARKS ===
The authenticated user, represented by an access token that describes its security context, is the main holder and has ultimate power against the default created desktop and window station objects in USER. The authenticated user in question
is the actual logged in user, this is the case when the server is impersonating a client. Administrators on the other hand have some share of power against default desktop but their power in question is extremely limited against the default
window station as admins can only just enumerate the available and valid handle stations within a desktop.
This commit is contained in:
George Bișoc 2022-03-16 21:03:56 +01:00
parent bee9b2fcc6
commit 878c2f4444
No known key found for this signature in database
GPG key ID: 688C4FBE25D7DEF6
10 changed files with 617 additions and 56 deletions

View file

@ -144,6 +144,7 @@ list(APPEND SOURCE
user/ntuser/prop.c
user/ntuser/scrollbar.c
user/ntuser/scrollex.c
user/ntuser/security.c
user/ntuser/session.c
user/ntuser/shutdown.c
user/ntuser/simplecall.c

View file

@ -87,6 +87,7 @@ DBG_CHANNEL DbgChannels[DbgChCount] = {
{L"UserProcess", DbgChUserProcess},
{L"UserProp", DbgChUserProp},
{L"UserScrollbar", DbgChUserScrollbar},
{L"UserSecurity", DbgChUserSecurity},
{L"UserShutdown", DbgChUserShutdown},
{L"UserSysparams", DbgChUserSysparams},
{L"UserTimer", DbgChUserTimer},

View file

@ -25,6 +25,7 @@
#include <ndk/mmfuncs.h>
#include <ndk/obfuncs.h>
#include <ndk/psfuncs.h>
#include <ndk/sefuncs.h>
#include <ndk/rtlfuncs.h>
#include <ntstrsafe.h>
#include <ntintsafe.h>

View file

@ -555,6 +555,7 @@ IntResolveDesktop(
LUID ProcessLuid;
USHORT StrSize;
SIZE_T MemSize;
PSECURITY_DESCRIPTOR ServiceSD = NULL;
POBJECT_ATTRIBUTES ObjectAttributes = NULL;
PUNICODE_STRING ObjectName;
UNICODE_STRING WinStaName, DesktopName;
@ -1012,16 +1013,29 @@ IntResolveDesktop(
}
ObjectName->Length = (USHORT)(wcslen(ObjectName->Buffer) * sizeof(WCHAR));
/*
* Set up a security descriptor for the service.
* A service is generally based upon a desktop
* and a window station. The newly created window
* station and desktop will get this security descriptor
* if such objects weren't created before.
*/
Status = IntCreateServiceSecurity(&ServiceSD);
if (!NT_SUCCESS(Status))
{
ERR("Failed to create a security descriptor for default window station, Status 0x%08lx\n", Status);
goto Quit;
}
/*
* 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);
ServiceSD);
Status = IntCreateWindowStation(&hWinSta,
ObjectAttributes,
@ -1054,8 +1068,11 @@ IntResolveDesktop(
}
ObjectName->Length = (USHORT)(wcslen(ObjectName->Buffer) * sizeof(WCHAR));
/* NOTE: The non-interactive desktop handle is never inheritable. */
// FIXME: Set security!
/*
* NOTE: The non-interactive desktop handle is never inheritable.
* The security descriptor is inherited from the newly created
* window station for the desktop.
*/
InitializeObjectAttributes(ObjectAttributes,
ObjectName,
OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
@ -1175,6 +1192,8 @@ Quit:
{
*phWinSta = hWinSta;
*phDesktop = hDesktop;
IntFreeSecurityBuffer(ServiceSD);
return STATUS_SUCCESS;
}
else
@ -1191,6 +1210,9 @@ Quit:
if (hWinSta)
ObCloseHandle(hWinSta, UserMode);
if (ServiceSD)
IntFreeSecurityBuffer(ServiceSD);
SetLastNtError(Status);
return Status;
}

View file

@ -55,32 +55,6 @@ typedef struct _DESKTOP
#define DT_GWL_PROCESSID 0
#define DT_GWL_THREADID 4
#define DESKTOP_READ STANDARD_RIGHTS_READ | \
DESKTOP_ENUMERATE | \
DESKTOP_READOBJECTS
#define DESKTOP_WRITE STANDARD_RIGHTS_WRITE | \
DESKTOP_CREATEMENU | \
DESKTOP_CREATEWINDOW | \
DESKTOP_HOOKCONTROL | \
DESKTOP_JOURNALPLAYBACK | \
DESKTOP_JOURNALRECORD | \
DESKTOP_WRITEOBJECTS
#define DESKTOP_EXECUTE STANDARD_RIGHTS_EXECUTE | \
DESKTOP_SWITCHDESKTOP
#define DESKTOP_ALL_ACCESS STANDARD_RIGHTS_REQUIRED | \
DESKTOP_CREATEMENU | \
DESKTOP_CREATEWINDOW | \
DESKTOP_ENUMERATE | \
DESKTOP_HOOKCONTROL | \
DESKTOP_JOURNALPLAYBACK | \
DESKTOP_JOURNALRECORD | \
DESKTOP_READOBJECTS | \
DESKTOP_SWITCHDESKTOP | \
DESKTOP_WRITEOBJECTS
extern PDESKTOP gpdeskInputDesktop;
extern PCLS DesktopWindowClass;
extern HDC ScreenDeviceContext;

View file

@ -0,0 +1,493 @@
/*
* PROJECT: ReactOS Win32k subsystem
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Security infrastructure of NTUSER component of Win32k
* COPYRIGHT: Copyright 2022 George Bișoc <george.bisoc@reactos.org>
*/
/* INCLUDES ******************************************************************/
#include <win32k.h>
DBG_DEFAULT_CHANNEL(UserSecurity);
/* FUNCTIONS *****************************************************************/
/**
* @brief
* Opens an access token that represents the effective security
* context of the caller. The purpose of this function is to query
* the authenticated user that is associated with the security
* context.
*
* @return
* Returns a handle to an opened access token that represents the
* security context of the authenticated user, otherwise NULL.
*/
HANDLE
IntGetCurrentAccessToken(VOID)
{
NTSTATUS Status;
HANDLE TokenHandle;
/*
* Try acquiring the security context by opening
* the current thread (or so called impersonation)
* token. Such token represents the effective caller.
* Otherwise if the current thread does not have a
* token (hence no impersonation occurs) then open
* the token of main calling process instead.
*/
Status = ZwOpenThreadToken(ZwCurrentThread(),
TOKEN_QUERY,
FALSE,
&TokenHandle);
if (!NT_SUCCESS(Status))
{
/*
* We might likely fail to open the thread
* token if the process isn't impersonating
* a client. In scenarios where the server
* isn't impersonating, open the main process
* token.
*/
if (Status == STATUS_NO_TOKEN)
{
TRACE("IntGetCurrentAccessToken(): The thread doesn't have a token, trying to open the process one...\n");
Status = ZwOpenProcessToken(ZwCurrentProcess(),
TOKEN_QUERY,
&TokenHandle);
if (!NT_SUCCESS(Status))
{
/* We failed opening process token as well, bail out... */
ERR("IntGetCurrentAccessToken(): Failed to capture security context, couldn't open the process token (Status 0x%08lx)\n", Status);
return NULL;
}
/* Return the opened token handle */
return TokenHandle;
}
/* There's a thread token but we couldn't open it so bail out */
ERR("IntGetCurrentAccessToken(): Failed to capture security context, couldn't open the thread token (Status 0x%08lx)\n", Status);
return NULL;
}
/* Return the opened token handle */
return TokenHandle;
}
/**
* @brief
* Allocates a buffer within UM (user mode) address
* space area. Such buffer is reserved for security
* purposes, such as allocating a buffer for a DACL
* or a security descriptor.
*
* @param[in] Length
* The length of the buffer that has to be allocated,
* in bytes.
*
* @return
* Returns a pointer to an allocated buffer whose
* contents are arbitrary. If the function fails,
* it means no pages are available to reserve for
* memory allocation for this buffer.
*/
PVOID
IntAllocateSecurityBuffer(
_In_ SIZE_T Length)
{
NTSTATUS Status;
PVOID Buffer = NULL;
/* Allocate the buffer in UM memory space */
Status = ZwAllocateVirtualMemory(ZwCurrentProcess(),
&Buffer,
0,
&Length,
MEM_COMMIT,
PAGE_READWRITE);
if (!NT_SUCCESS(Status))
{
ERR("IntAllocateSecurityBuffer(): Failed to allocate the buffer (Status 0x%08lx)\n", Status);
return NULL;
}
return Buffer;
}
/**
* @brief
* Frees an allocated security buffer from UM
* memory that is been previously allocated by
* IntAllocateSecurityBuffer function.
*
* @param[in] Buffer
* A pointer to a buffer whose contents are
* arbitrary, to be freed from UM memory space.
*
* @return
* Nothing.
*/
VOID
IntFreeSecurityBuffer(
_In_ PVOID Buffer)
{
SIZE_T Size = 0;
ZwFreeVirtualMemory(ZwCurrentProcess(),
&Buffer,
&Size,
MEM_RELEASE);
}
/**
* @brief
* Queries the authenticated user security identifier
* (SID) that is associated with the security context
* of the access token that is being opened.
*
* @param[out] User
* A pointer to the token user that contains the security
* identifier of the authenticated user.
*
* @return
* Returns STATUS_SUCCESS if the function has successfully
* queried the token user. STATUS_UNSUCCESSFUL is returned
* if the effective token of the caller couldn't be opened.
* STATUS_NO_MEMORY is returned if memory allocation for
* token user buffer has failed because of lack of necessary
* pages reserved for such allocation. A failure NTSTATUS
* code is returned otherwise.
*
* @remarks
* !!!WARNING!!! -- THE CALLER WHO QUERIES THE TOKEN USER IS
* RESPONSIBLE TO FREE THE ALLOCATED TOKEN USER BUFFER THAT IS
* BEING GIVEN.
*/
NTSTATUS
IntQueryUserSecurityIdentification(
_Out_ PTOKEN_USER *User)
{
NTSTATUS Status;
PTOKEN_USER UserToken;
HANDLE Token;
ULONG BufferLength;
/* Initialize the parameter */
*User = NULL;
/* Open the current token of the caller */
Token = IntGetCurrentAccessToken();
if (!Token)
{
ERR("IntQueryUserSecurityIdentification(): Couldn't capture the token!\n");
return STATUS_UNSUCCESSFUL;
}
/*
* Since we do not know what the length
* of the buffer size should be exactly to
* hold the user data, let the function
* tell us the size.
*/
Status = ZwQueryInformationToken(Token,
TokenUser,
NULL,
0,
&BufferLength);
if (!NT_SUCCESS(Status) && Status == STATUS_BUFFER_TOO_SMALL)
{
/*
* Allocate some memory for the buffer
* based on the size that the function
* gave us.
*/
UserToken = IntAllocateSecurityBuffer(BufferLength);
if (!UserToken)
{
/* Bail out if we failed */
ERR("IntQueryUserSecurityIdentification(): Couldn't allocate memory for the token user!\n");
ZwClose(Token);
return STATUS_NO_MEMORY;
}
}
/* Query the user now as we have plenty of space to hold it */
Status = ZwQueryInformationToken(Token,
TokenUser,
UserToken,
BufferLength,
&BufferLength);
if (!NT_SUCCESS(Status))
{
/* We failed, bail out */
ERR("IntQueryUserSecurityIdentification(): Failed to query token user (Status 0x%08lx)\n", Status);
IntFreeSecurityBuffer(UserToken);
ZwClose(Token);
return Status;
}
/* All good, give the buffer to the caller and close the captured token */
*User = UserToken;
ZwClose(Token);
return STATUS_SUCCESS;
}
/**
* @brief
* Creates a security descriptor for the service.
*
* @param[out] ServiceSd
* A pointer to a newly allocated and created security
* descriptor for the service.
*
* @return
* Returns STATUS_SUCCESS if the function has successfully
* queried created the security descriptor. STATUS_NO_MEMORY
* is returned if memory allocation for security buffers because
* of a lack of needed pages to reserve for such allocation. A
* failure NTSTATUS code is returned otherwise.
*/
NTSTATUS
NTAPI
IntCreateServiceSecurity(
_Out_ PSECURITY_DESCRIPTOR *ServiceSd)
{
NTSTATUS Status;
PACL ServiceDacl;
ULONG DaclSize;
ULONG RelSDSize;
SECURITY_DESCRIPTOR AbsSD;
PSECURITY_DESCRIPTOR RelSD;
PTOKEN_USER TokenUser;
/* Initialize our local variables */
RelSDSize = 0;
TokenUser = NULL;
RelSD = NULL;
ServiceDacl = NULL;
/* Query the logged in user of the current security context (aka token) */
Status = IntQueryUserSecurityIdentification(&TokenUser);
if (!TokenUser)
{
ERR("IntCreateServiceSecurity(): Failed to query the token user (Status 0x%08lx)\n", Status);
return Status;
}
/* Initialize the absolute security descriptor */
Status = RtlCreateSecurityDescriptor(&AbsSD, SECURITY_DESCRIPTOR_REVISION);
if (!NT_SUCCESS(Status))
{
ERR("IntCreateServiceSecurity(): Failed to initialize absolute SD (Status 0x%08lx)\n", Status);
goto Quit;
}
/*
* Build up the size of access control
* list (the DACL) necessary to initialize
* our ACL. The first two entry members
* of ACL field are the authenticated user
* that is associated with the security
* context of the token. Then here come
* the last two entries which are admins.
* Why the ACL contains two ACEs of the
* same SID is because of service access
* rights and ACE inheritance.
*
* A service is composed of a default
* desktop and window station upon
* booting the system. On Windows connection
* to such service is being made if no
* default window station and desktop handles
* were created before. The desktop and winsta
* objects grant access on a separate type basis.
* The user is granted full access to the window
* station first and then full access to the desktop.
* After that admins are granted specific rights
* separately, just like the user. Ultimately the
* ACEs that handle desktop rights management are
* inherited to the default desktop object so
* that there's no need to have a separate security
* descriptor for the desktop object alone.
*/
DaclSize = sizeof(ACL) +
sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(TokenUser->User.Sid) +
sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(TokenUser->User.Sid) +
sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(SeExports->SeAliasAdminsSid) +
sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(SeExports->SeAliasAdminsSid);
/* Allocate memory for service DACL */
ServiceDacl = IntAllocateSecurityBuffer(DaclSize);
if (!ServiceDacl)
{
ERR("IntCreateServiceSecurity(): Failed to allocate memory for service DACL!\n");
Status = STATUS_NO_MEMORY;
goto Quit;
}
/* Now create the DACL */
Status = RtlCreateAcl(ServiceDacl,
DaclSize,
ACL_REVISION);
if (!NT_SUCCESS(Status))
{
ERR("IntCreateServiceSecurity(): Failed to create service DACL (Status 0x%08lx)\n", Status);
goto Quit;
}
/*
* The authenticated user is the ultimate and absolute
* king in charge of the created (or opened, whatever that is)
* window station object.
*/
Status = RtlAddAccessAllowedAceEx(ServiceDacl,
ACL_REVISION,
0,
WINSTA_ACCESS_ALL,
TokenUser->User.Sid);
if (!NT_SUCCESS(Status))
{
ERR("IntCreateServiceSecurity(): Failed to set up window station ACE for authenticated user (Status 0x%08lx)\n", Status);
goto Quit;
}
/*
* The authenticated user also has the ultimate power
* over the desktop object as well. This ACE cannot
* be propagated but inherited. See the comment
* above regarding ACL size for further explanation.
*/
Status = RtlAddAccessAllowedAceEx(ServiceDacl,
ACL_REVISION,
INHERIT_ONLY_ACE | NO_PROPAGATE_INHERIT_ACE | OBJECT_INHERIT_ACE,
DESKTOP_ALL_ACCESS,
TokenUser->User.Sid);
if (!NT_SUCCESS(Status))
{
ERR("IntCreateServiceSecurity(): Failed to set up desktop ACE for authenticated user (Status 0x%08lx)\n", Status);
goto Quit;
}
/*
* Administrators can only enumerate window
* stations within a desktop.
*/
Status = RtlAddAccessAllowedAceEx(ServiceDacl,
ACL_REVISION,
0,
WINSTA_ENUMERATE,
SeExports->SeAliasAdminsSid);
if (!NT_SUCCESS(Status))
{
ERR("IntCreateServiceSecurity(): Failed to set up window station ACE for admins (Status 0x%08lx)\n", Status);
goto Quit;
}
/*
* Administrators have some share of power over
* the desktop object. They can enumerate desktops,
* write and read upon the object itself.
*/
Status = RtlAddAccessAllowedAceEx(ServiceDacl,
ACL_REVISION,
INHERIT_ONLY_ACE | NO_PROPAGATE_INHERIT_ACE | OBJECT_INHERIT_ACE,
DESKTOP_ENUMERATE | DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS,
SeExports->SeAliasAdminsSid);
if (!NT_SUCCESS(Status))
{
ERR("IntCreateServiceSecurity(): Failed to set up desktop ACE for admins (Status 0x%08lx)\n", Status);
goto Quit;
}
/* Set the DACL to absolute SD */
Status = RtlSetDaclSecurityDescriptor(&AbsSD,
TRUE,
ServiceDacl,
FALSE);
if (!NT_SUCCESS(Status))
{
ERR("IntCreateServiceSecurity(): Failed to set up service DACL to absolute SD (Status 0x%08lx)\n", Status);
goto Quit;
}
/* This descriptor is ownerless */
Status = RtlSetOwnerSecurityDescriptor(&AbsSD,
NULL,
FALSE);
if (!NT_SUCCESS(Status))
{
ERR("IntCreateServiceSecurity(): Failed to make the absolute SD as ownerless (Status 0x%08lx)\n", Status);
goto Quit;
}
/* This descriptor has no primary group */
Status = RtlSetGroupSecurityDescriptor(&AbsSD,
NULL,
FALSE);
if (!NT_SUCCESS(Status))
{
ERR("IntCreateServiceSecurity(): Failed to make the absolute SD as having no primary group (Status 0x%08lx)\n", Status);
goto Quit;
}
/*
* Determine how much size is needed to allocate
* memory space for our relative security descriptor.
*/
Status = RtlAbsoluteToSelfRelativeSD(&AbsSD,
NULL,
&RelSDSize);
if (Status != STATUS_BUFFER_TOO_SMALL)
{
ERR("IntCreateServiceSecurity(): Unexpected status code, must be STATUS_BUFFER_TOO_SMALL (Status 0x%08lx)\n", Status);
goto Quit;
}
/* Allocate memory for this */
RelSD = IntAllocateSecurityBuffer(RelSDSize);
if (!RelSD)
{
ERR("IntCreateServiceSecurity(): Failed to allocate memory pool for relative SD!\n");
Status = STATUS_NO_MEMORY;
goto Quit;
}
/* Convert the absolute SD into a relative one now */
Status = RtlAbsoluteToSelfRelativeSD(&AbsSD,
RelSD,
&RelSDSize);
if (!NT_SUCCESS(Status))
{
ERR("IntCreateServiceSecurity(): Failed to convert absolute SD to a relative one (Status 0x%08lx)\n", Status);
goto Quit;
}
/* All good, give the SD to the caller */
*ServiceSd = RelSD;
Quit:
if (ServiceDacl)
{
IntFreeSecurityBuffer(ServiceDacl);
}
if (TokenUser)
{
IntFreeSecurityBuffer(TokenUser);
}
if (!NT_SUCCESS(Status))
{
if (RelSD)
{
IntFreeSecurityBuffer(RelSD);
}
}
return Status;
}
/* EOF */

View file

@ -0,0 +1,92 @@
/*
* PROJECT: ReactOS Win32k subsystem
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Security infrastructure of NTUSER component of Win32k
* COPYRIGHT: Copyright 2022 George Bișoc <george.bisoc@reactos.org>
*/
#pragma once
//
// USER objects security rights
//
/* Desktop access rights */
#define DESKTOP_READ (STANDARD_RIGHTS_READ | \
DESKTOP_ENUMERATE | \
DESKTOP_READOBJECTS)
#define DESKTOP_WRITE (STANDARD_RIGHTS_WRITE | \
DESKTOP_CREATEMENU | \
DESKTOP_CREATEWINDOW | \
DESKTOP_HOOKCONTROL | \
DESKTOP_JOURNALPLAYBACK | \
DESKTOP_JOURNALRECORD | \
DESKTOP_WRITEOBJECTS)
#define DESKTOP_EXECUTE (STANDARD_RIGHTS_EXECUTE | \
DESKTOP_SWITCHDESKTOP)
#define DESKTOP_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \
DESKTOP_CREATEMENU | \
DESKTOP_CREATEWINDOW | \
DESKTOP_ENUMERATE | \
DESKTOP_HOOKCONTROL | \
DESKTOP_JOURNALPLAYBACK | \
DESKTOP_JOURNALRECORD | \
DESKTOP_READOBJECTS | \
DESKTOP_SWITCHDESKTOP | \
DESKTOP_WRITEOBJECTS)
/* Window Station access rights */
#define WINSTA_READ (STANDARD_RIGHTS_READ | \
WINSTA_ENUMDESKTOPS | \
WINSTA_ENUMERATE | \
WINSTA_READATTRIBUTES | \
WINSTA_READSCREEN)
#define WINSTA_WRITE (STANDARD_RIGHTS_WRITE | \
WINSTA_ACCESSCLIPBOARD | \
WINSTA_CREATEDESKTOP | \
WINSTA_WRITEATTRIBUTES)
#define WINSTA_EXECUTE (STANDARD_RIGHTS_EXECUTE | \
WINSTA_ACCESSGLOBALATOMS | \
WINSTA_EXITWINDOWS)
#define WINSTA_ACCESS_ALL (STANDARD_RIGHTS_REQUIRED | \
WINSTA_ACCESSCLIPBOARD | \
WINSTA_ACCESSGLOBALATOMS | \
WINSTA_CREATEDESKTOP | \
WINSTA_ENUMDESKTOPS | \
WINSTA_ENUMERATE | \
WINSTA_EXITWINDOWS | \
WINSTA_READATTRIBUTES | \
WINSTA_READSCREEN | \
WINSTA_WRITEATTRIBUTES)
//
// Function prototypes
//
HANDLE
IntCaptureCurrentAccessToken(VOID);
PVOID
IntAllocateSecurityBuffer(
_In_ SIZE_T Length);
VOID
IntFreeSecurityBuffer(
_In_ PVOID Buffer);
NTSTATUS
IntQueryUserSecurityIdentification(
_Out_ PTOKEN_USER *User);
NTSTATUS
NTAPI
IntCreateServiceSecurity(
_Out_ PSECURITY_DESCRIPTOR *ServiceSd);
/* EOF */

View file

@ -108,6 +108,7 @@
DbgChUserProcess,
DbgChUserProp,
DbgChUserScrollbar,
DbgChUserSecurity,
DbgChUserShutdown,
DbgChUserSysparams,
DbgChUserThread,

View file

@ -47,32 +47,6 @@ extern HANDLE gpidLogon;
extern HWND hwndSAS;
extern UNICODE_STRING gustrWindowStationsDir;
#define WINSTA_READ STANDARD_RIGHTS_READ | \
WINSTA_ENUMDESKTOPS | \
WINSTA_ENUMERATE | \
WINSTA_READATTRIBUTES | \
WINSTA_READSCREEN
#define WINSTA_WRITE STANDARD_RIGHTS_WRITE | \
WINSTA_ACCESSCLIPBOARD | \
WINSTA_CREATEDESKTOP | \
WINSTA_WRITEATTRIBUTES
#define WINSTA_EXECUTE STANDARD_RIGHTS_EXECUTE | \
WINSTA_ACCESSGLOBALATOMS | \
WINSTA_EXITWINDOWS
#define WINSTA_ACCESS_ALL STANDARD_RIGHTS_REQUIRED | \
WINSTA_ACCESSCLIPBOARD | \
WINSTA_ACCESSGLOBALATOMS | \
WINSTA_CREATEDESKTOP | \
WINSTA_ENUMDESKTOPS | \
WINSTA_ENUMERATE | \
WINSTA_EXITWINDOWS | \
WINSTA_READATTRIBUTES | \
WINSTA_READSCREEN | \
WINSTA_WRITEATTRIBUTES
CODE_SEG("INIT")
NTSTATUS
NTAPI
@ -125,4 +99,5 @@ BOOL FASTCALL UserSetProcessWindowStation(HWINSTA hWindowStation);
BOOL FASTCALL co_IntInitializeDesktopGraphics(VOID);
VOID FASTCALL IntEndDesktopGraphics(VOID);
BOOL FASTCALL CheckWinstaAttributeAccess(ACCESS_MASK);
/* EOF */

View file

@ -81,6 +81,7 @@ typedef struct _DC *PDC;
#include "user/ntuser/painting.h"
#include "user/ntuser/class.h"
#include "user/ntuser/window.h"
#include "user/ntuser/security.h"
#include "user/ntuser/sysparams.h"
#include "user/ntuser/prop.h"
#include "user/ntuser/guicheck.h"