Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566/* * 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-2023 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. */HANDLEIntGetCurrentAccessToken(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. */PVOIDIntAllocateSecurityBuffer( _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. */VOIDIntFreeSecurityBuffer( _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. */NTSTATUSIntQueryUserSecurityIdentification( _Out_ PTOKEN_USER *User){ NTSTATUS Status; PTOKEN_USER UserToken = NULL; 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 (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; } } else if (!NT_SUCCESS(Status)) { ERR("IntQueryUserSecurityIdentification(): Failed to query the necessary length for the buffer (Status 0x%08lx)!\n", Status); ZwClose(Token); return Status; }
/* 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 * Assigns a security descriptor to the desktop * object during a desktop object parse procedure. * * @param[in] WinSta * A pointer to a window station object, of which * such object contains its own security descriptor * that will be captured. * * @param[in] Desktop * A pointer to a desktop object that is created * during a parse procedure. * * @param[in] AccessState * A pointer to an access state structure that * describes the progress state of an access in * action. * * @return * Returns STATUS_SUCCESS if the function has successfully * assigned new security descriptor to the desktop object. * A NTSTATUS failure code is returned otherwise. */NTSTATUSNTAPIIntAssignDesktopSecurityOnParse( _In_ PWINSTATION_OBJECT WinSta, _In_ PDESKTOP Desktop, _In_ PACCESS_STATE AccessState){ NTSTATUS Status; PSECURITY_DESCRIPTOR CapturedDescriptor; BOOLEAN MemoryAllocated;
/* * Capture the security descriptor from * the window station. The window station * in question has a descriptor that is * inheritable and contains desktop access * rights as well. */ Status = ObGetObjectSecurity(WinSta, &CapturedDescriptor, &MemoryAllocated); if (!NT_SUCCESS(Status)) { ERR("IntAssignDesktopSecurityOnParse(): Failed to capture the security descriptor from window station (Status 0x%08lx)\n", Status); return Status; }
/* Assign new security to the desktop */ Status = ObAssignSecurity(AccessState, CapturedDescriptor, Desktop, ExDesktopObjectType); if (!NT_SUCCESS(Status)) { ERR("IntAssignDesktopSecurityOnParse(): Failed to assign security information to the desktop object (Status 0x%08lx)\n", Status); }
/* Release the descriptor that we have captured */ ObReleaseObjectSecurity(CapturedDescriptor, MemoryAllocated); return Status;}
/** * @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. */NTSTATUSNTAPIIntCreateServiceSecurity( _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 */