12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376 |
- /*++
- Copyright (c) 2014 Minoca Corp. All Rights Reserved
- Module Name:
- perm.c
- Abstract:
- This module implements support routines for thread permission and identity
- management.
- Author:
- Evan Green 4-Dec-2014
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/kernel/kernel.h>
- #include "psp.h"
- //
- // This macro evaluates to non-zero if the given user ID matches any of
- // the real user ID, effective user ID, or saved user ID of the given identity.
- //
- #define MATCHES_IDENTITY_USER(_UserId, _Identity) \
- (((_UserId) == ((_Identity)->RealUserId)) || \
- ((_UserId) == ((_Identity)->EffectiveUserId)) || \
- ((_UserId) == ((_Identity)->SavedUserId)))
- //
- // This macro evaluates to non-zero if the given group ID matches any of the
- // real group ID, effective group ID, or saved group ID of the given identity.
- //
- #define MATCHES_IDENTITY_GROUP(_GroupId, _Identity) \
- (((_GroupId) == ((_Identity)->RealGroupId)) || \
- ((_GroupId) == ((_Identity)->EffectiveGroupId)) || \
- ((_GroupId) == ((_Identity)->SavedGroupId)))
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- KSTATUS
- PspSetThreadIdentity (
- ULONG FieldsToSet,
- PTHREAD_IDENTITY Identity
- );
- KSTATUS
- PspSetThreadPermissions (
- ULONG FieldsToSet,
- PTHREAD_PERMISSIONS Permissions
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // ------------------------------------------------------------------ Functions
- //
- KERNEL_API
- KSTATUS
- PsCheckPermission (
- ULONG Permission
- )
- /*++
- Routine Description:
- This routine checks to see if the calling thread currently has the given
- permission.
- Arguments:
- Permission - Supplies the permission number to check. See PERMISSION_*
- definitions.
- Return Value:
- STATUS_SUCCESS if the current thread has the given permission.
- STATUS_PERMISSION_DENIED if the thread does not have the given permission.
- --*/
- {
- PKTHREAD Thread;
- Thread = KeGetCurrentThread();
- if (PERMISSION_CHECK(Thread->Permissions.Effective, Permission)) {
- return STATUS_SUCCESS;
- }
- return STATUS_PERMISSION_DENIED;
- }
- BOOL
- PsIsUserInGroup (
- GROUP_ID Group
- )
- /*++
- Routine Description:
- This routine determines if the given group ID matches the effective
- group ID or any of the supplementary group IDs of the calling thread. The
- current thread must not be a kernel thread.
- Arguments:
- Group - Supplies the group ID to check against.
- Return Value:
- TRUE if the calling thread is a member of the given group.
- FALSE if the calling thread is not a member of the given group.
- --*/
- {
- UINTN GroupIndex;
- PSUPPLEMENTARY_GROUPS SupplementaryGroups;
- PKTHREAD Thread;
- Thread = KeGetCurrentThread();
- if ((Thread->Flags & THREAD_FLAG_USER_MODE) == 0) {
- ASSERT(FALSE);
- return FALSE;
- }
- if (Thread->Identity.EffectiveGroupId == Group) {
- return TRUE;
- }
- SupplementaryGroups = Thread->SupplementaryGroups;
- while (SupplementaryGroups != NULL) {
- for (GroupIndex = 0;
- GroupIndex < SupplementaryGroups->Count;
- GroupIndex += 1) {
- if (Group == SupplementaryGroups->Groups[GroupIndex]) {
- return TRUE;
- }
- }
- SupplementaryGroups = SupplementaryGroups->Next;
- }
- return FALSE;
- }
- VOID
- PsSysSetThreadIdentity (
- ULONG SystemCallNumber,
- PVOID SystemCallParameter,
- PTRAP_FRAME TrapFrame,
- PULONG ResultSize
- )
- /*++
- Routine Description:
- This routine implements the get/set thread identity system call.
- Arguments:
- SystemCallNumber - Supplies the system call number that was requested.
- SystemCallParameter - Supplies a pointer to the parameters supplied with
- the system call. This structure will be a stack-local copy of the
- actual parameters passed from user-mode.
- TrapFrame - Supplies a pointer to the trap frame generated by this jump
- from user mode to kernel mode.
- ResultSize - Supplies a pointer where the system call routine returns the
- size of the parameter structure to be copied back to user mode. The
- value returned here must be no larger than the original parameter
- structure size. The default is the original size of the parameters.
- Return Value:
- None.
- --*/
- {
- PSYSTEM_CALL_SET_THREAD_IDENTITY Parameters;
- ASSERT(SystemCallNumber == SystemCallSetThreadIdentity);
- Parameters = SystemCallParameter;
- Parameters->Status = PspSetThreadIdentity(Parameters->Request.FieldsToSet,
- &(Parameters->Request.Identity));
- return;
- }
- VOID
- PsSysSetThreadPermissions (
- ULONG SystemCallNumber,
- PVOID SystemCallParameter,
- PTRAP_FRAME TrapFrame,
- PULONG ResultSize
- )
- /*++
- Routine Description:
- This routine implements the get/set thread permissions system call.
- Arguments:
- SystemCallNumber - Supplies the system call number that was requested.
- SystemCallParameter - Supplies a pointer to the parameters supplied with
- the system call. This structure will be a stack-local copy of the
- actual parameters passed from user-mode.
- TrapFrame - Supplies a pointer to the trap frame generated by this jump
- from user mode to kernel mode.
- ResultSize - Supplies a pointer where the system call routine returns the
- size of the parameter structure to be copied back to user mode. The
- value returned here must be no larger than the original parameter
- structure size. The default is the original size of the parameters.
- Return Value:
- None.
- --*/
- {
- PSYSTEM_CALL_SET_THREAD_PERMISSIONS Parameters;
- ASSERT(SystemCallNumber == SystemCallSetThreadPermissions);
- Parameters = SystemCallParameter;
- Parameters->Status = PspSetThreadPermissions(
- Parameters->Request.FieldsToSet,
- &(Parameters->Request.Permissions));
- return;
- }
- VOID
- PsSysSetSupplementaryGroups (
- ULONG SystemCallNumber,
- PVOID SystemCallParameter,
- PTRAP_FRAME TrapFrame,
- PULONG ResultSize
- )
- /*++
- Routine Description:
- This routine implements the get/set supplementary groups system call.
- Arguments:
- SystemCallNumber - Supplies the system call number that was requested.
- SystemCallParameter - Supplies a pointer to the parameters supplied with
- the system call. This structure will be a stack-local copy of the
- actual parameters passed from user-mode.
- TrapFrame - Supplies a pointer to the trap frame generated by this jump
- from user mode to kernel mode.
- ResultSize - Supplies a pointer where the system call routine returns the
- size of the parameter structure to be copied back to user mode. The
- value returned here must be no larger than the original parameter
- structure size. The default is the original size of the parameters.
- Return Value:
- None.
- --*/
- {
- UINTN AllocationSize;
- PSUPPLEMENTARY_GROUPS Block;
- UINTN BlockCapacity;
- UINTN BlockIndex;
- UINTN Count;
- PSUPPLEMENTARY_GROUPS NewBlock;
- PSYSTEM_CALL_SET_SUPPLEMENTARY_GROUPS Parameters;
- PKPROCESS Process;
- KSTATUS Status;
- PKTHREAD Thread;
- ASSERT(SystemCallNumber == SystemCallSetSupplementaryGroups);
- NewBlock = NULL;
- Thread = KeGetCurrentThread();
- Process = PsGetCurrentProcess();
- Parameters = SystemCallParameter;
- if (Parameters->Set != FALSE) {
- //
- // Set an (arbitrary) cap.
- //
- if (Parameters->Count > SUPPLEMENTARY_GROUP_MAX) {
- Status = STATUS_INVALID_PARAMETER;
- goto SysSetSupplementaryGroupsEnd;
- }
- //
- // Ensure the caller has the privileges to do this.
- //
- Status = PsCheckPermission(PERMISSION_SET_GROUP_ID);
- if (!KSUCCESS(Status)) {
- goto SysSetSupplementaryGroupsEnd;
- }
- //
- // Count the current capacity.
- //
- Count = 0;
- Block = Thread->SupplementaryGroups;
- while (Block != NULL) {
- Count += Block->Capacity;
- Block = Block->Next;
- }
- //
- // Allocate a new block if needed.
- //
- if (Count < Parameters->Count) {
- BlockCapacity = Parameters->Count - Count;
- BlockCapacity = ALIGN_RANGE_UP(BlockCapacity,
- SUPPLEMENTARY_GROUP_MIN);
- AllocationSize = sizeof(SUPPLEMENTARY_GROUPS) +
- (BlockCapacity * sizeof(GROUP_ID));
- NewBlock = MmAllocatePagedPool(AllocationSize,
- PS_GROUP_ALLOCATION_TAG);
- if (NewBlock == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto SysSetSupplementaryGroupsEnd;
- }
- //
- // Initialize to all ones instead of zeros to avoid
- // accidents that assign groups with IDs of root.
- //
- RtlSetMemory(NewBlock, -1, AllocationSize);
- NewBlock->Capacity = BlockCapacity;
- NewBlock->Groups = (PGROUP_ID)(NewBlock + 1);
- NewBlock->Count = 0;
- }
- KeAcquireQueuedLock(Process->QueuedLock);
- Status = STATUS_SUCCESS;
- Count = 0;
- Block = Thread->SupplementaryGroups;
- while (Block != NULL) {
- //
- // Set each entry in the block, up to the total count.
- //
- for (BlockIndex = 0;
- BlockIndex < Block->Capacity;
- BlockIndex += 1) {
- if (Count == Parameters->Count) {
- break;
- }
- Status = MmCopyFromUserMode(&(Block->Groups[BlockIndex]),
- &(Parameters->Groups[Count]),
- sizeof(GROUP_ID));
- if (!KSUCCESS(Status)) {
- break;
- }
- Count += 1;
- }
- //
- // Set the number of valid entries to be however many were filled
- // in.
- //
- Block->Count = BlockIndex;
- if (!KSUCCESS(Status)) {
- break;
- }
- Block = Block->Next;
- }
- //
- // Add the remainder of the groups to the brand new block.
- //
- if (Count < Parameters->Count) {
- ASSERT((NewBlock != NULL) &&
- ((Parameters->Count - Count) <= NewBlock->Capacity));
- BlockIndex = 0;
- while (Count < Parameters->Count) {
- Status = MmCopyFromUserMode(&(NewBlock->Groups[BlockIndex]),
- &(Parameters->Groups[Count]),
- sizeof(GROUP_ID));
- if (!KSUCCESS(Status)) {
- break;
- }
- Count += 1;
- BlockIndex += 1;
- }
- //
- // Only add the new block if it worked, otherwise memory could
- // accumulate via user mode calls with bad pointers.
- //
- NewBlock->Count = BlockIndex;
- if (KSUCCESS(Status)) {
- NewBlock->Next = Thread->SupplementaryGroups;
- Thread->SupplementaryGroups = NewBlock;
- NewBlock = NULL;
- }
- }
- KeReleaseQueuedLock(Process->QueuedLock);
- //
- // Just get the groups.
- //
- } else {
- Count = 0;
- Block = Thread->SupplementaryGroups;
- Status = STATUS_SUCCESS;
- while (Block != NULL) {
- for (BlockIndex = 0; BlockIndex < Block->Count; BlockIndex += 1) {
- if ((Count < Parameters->Count) && (KSUCCESS(Status))) {
- Status = MmCopyToUserMode(&(Parameters->Groups[Count]),
- &(Block->Groups[BlockIndex]),
- sizeof(GROUP_ID));
- }
- Count += 1;
- }
- Block = Block->Next;
- }
- Parameters->Count = Count;
- }
- SysSetSupplementaryGroupsEnd:
- if (NewBlock != NULL) {
- MmFreePagedPool(NewBlock);
- }
- Parameters->Status = Status;
- return;
- }
- VOID
- PsSysSetResourceLimit (
- ULONG SystemCallNumber,
- PVOID SystemCallParameter,
- PTRAP_FRAME TrapFrame,
- PULONG ResultSize
- )
- /*++
- Routine Description:
- This routine implements the system call that gets or sets a resource limit
- for the current thread.
- Arguments:
- SystemCallNumber - Supplies the system call number that was requested.
- SystemCallParameter - Supplies a pointer to the parameters supplied with
- the system call. This structure will be a stack-local copy of the
- actual parameters passed from user-mode.
- TrapFrame - Supplies a pointer to the trap frame generated by this jump
- from user mode to kernel mode.
- ResultSize - Supplies a pointer where the system call routine returns the
- size of the parameter structure to be copied back to user mode. The
- value returned here must be no larger than the original parameter
- structure size. The default is the original size of the parameters.
- Return Value:
- None.
- --*/
- {
- RESOURCE_LIMIT NewValue;
- PSYSTEM_CALL_SET_RESOURCE_LIMIT Parameters;
- KSTATUS Status;
- PKTHREAD Thread;
- RESOURCE_LIMIT_TYPE Type;
- Thread = KeGetCurrentThread();
- Parameters = SystemCallParameter;
- Type = Parameters->Type;
- if ((Type >= ResourceLimitCount) || (Type < 0)) {
- Status = STATUS_INVALID_PARAMETER;
- goto SysSetResourceLimitEnd;
- }
- //
- // Copy the values to potentially set into a local, and copy the current
- // values to be returned.
- //
- NewValue.Current = Parameters->Value.Current;
- NewValue.Max = Parameters->Value.Max;
- Parameters->Value.Current = Thread->Limits[Type].Current;
- Parameters->Value.Max = Thread->Limits[Type].Max;
- //
- // If not setting, that's all there is to do.
- //
- if (Parameters->Set == FALSE) {
- Status = STATUS_SUCCESS;
- goto SysSetResourceLimitEnd;
- }
- //
- // The caller wants to set new limits. Make sure current isn't greater than
- // max.
- //
- if (NewValue.Current > NewValue.Max) {
- Status = STATUS_INVALID_PARAMETER;
- goto SysSetResourceLimitEnd;
- }
- //
- // If trying to raise the max, the caller had better have the appropriate
- // permissions.
- //
- if (NewValue.Max > Parameters->Value.Max) {
- Status = PsCheckPermission(PERMISSION_RESOURCES);
- if (!KSUCCESS(Status)) {
- goto SysSetResourceLimitEnd;
- }
- //
- // Don't allow the file count go beyond what the kernel can handle.
- //
- if ((Type == ResourceLimitFileCount) &&
- (NewValue.Max > OB_MAX_HANDLES)) {
- Status = STATUS_PERMISSION_DENIED;
- goto SysSetResourceLimitEnd;
- }
- }
- Thread->Limits[Type].Max = NewValue.Max;
- Thread->Limits[Type].Current = NewValue.Current;
- //
- // Attempt to set the new stack size now, and silently ignore failures.
- //
- if (Type == ResourceLimitStack) {
- if ((Thread->Flags & THREAD_FLAG_FREE_USER_STACK) != 0) {
- PspSetThreadUserStackSize(Thread, NewValue.Current);
- }
- }
- Status = STATUS_SUCCESS;
- SysSetResourceLimitEnd:
- Parameters->Status = Status;
- return;
- }
- VOID
- PspPerformExecutePermissionChanges (
- PIO_HANDLE ExecutableHandle
- )
- /*++
- Routine Description:
- This routine fixes up the user identity and potentially permissions in
- preparation for executing an image.
- Arguments:
- ExecutableHandle - Supplies an open file handle to the executable image.
- Return Value:
- None.
- --*/
- {
- BOOL FileEffective;
- PERMISSION_SET FileInheritable;
- PERMISSION_SET FilePermitted;
- FILE_PROPERTIES FileProperties;
- PERMISSION_SET NewPermitted;
- BOOL SetRoot;
- KSTATUS Status;
- PKTHREAD Thread;
- Thread = KeGetCurrentThread();
- FileEffective = FALSE;
- FileInheritable = PERMISSION_SET_EMPTY;
- FilePermitted = PERMISSION_SET_EMPTY;
- //
- // Always clear the keep capabilities bit.
- //
- Thread->Permissions.Behavior &= ~PERMISSION_BEHAVIOR_KEEP_PERMISSIONS;
- Status = IoGetFileInformation(ExecutableHandle, &FileProperties);
- if (!KSUCCESS(Status)) {
- //
- // Save the effective user and group IDs into the saved user and group
- // IDs.
- //
- Thread->Identity.SavedUserId = Thread->Identity.EffectiveUserId;
- Thread->Identity.SavedGroupId = Thread->Identity.EffectiveGroupId;
- return;
- }
- //
- // TODO: Return immediately if the mount flags specify no-setuid.
- //
- //
- // If the set-group-id bit is set in the file permissions, then change the
- // effective group ID to that of the file.
- //
- if ((FileProperties.Permissions & FILE_PERMISSION_SET_GROUP_ID) != 0) {
- Thread->Identity.EffectiveGroupId = FileProperties.GroupId;
- }
- //
- // If the set-user-id bit is set in the file permissions, then change the
- // effective user ID to that of the file.
- //
- SetRoot = FALSE;
- if ((FileProperties.Permissions & FILE_PERMISSION_SET_USER_ID) != 0) {
- Thread->Identity.EffectiveUserId = FileProperties.UserId;
- if (FileProperties.UserId == USER_ID_ROOT) {
- SetRoot = TRUE;
- }
- }
- //
- // Initialize the saved user and group IDs to be equal to the effective
- // ones.
- //
- Thread->Identity.SavedUserId = Thread->Identity.EffectiveUserId;
- Thread->Identity.SavedGroupId = Thread->Identity.EffectiveGroupId;
- if ((Thread->Permissions.Behavior & PERMISSION_BEHAVIOR_NO_ROOT) == 0) {
- //
- // If it's a set-user-id-root program, or the real user ID is root, and
- // the user hasn't set the no-root flag, then adjust the permissions
- // mask.
- //
- if ((SetRoot != FALSE) ||
- (Thread->Identity.RealUserId == USER_ID_ROOT)) {
- FilePermitted = PERMISSION_SET_FULL;
- FileInheritable = PERMISSION_SET_FULL;
- }
- //
- // If the new effective user is root, either by setuid methods or just
- // because they were before, then the file effective bit is set so that
- // they have these permissions on startup.
- //
- if (Thread->Identity.EffectiveUserId == USER_ID_ROOT) {
- FileEffective = TRUE;
- }
- }
- //
- // Modify the permission sets for the execution. The new permitted mask is
- // (OldPermitted & FileInheritable) | (FilePermited & Limit). The effective
- // permissions are set to the permitted permissions if the file "effective"
- // bit is set, or just wiped otherwise.
- //
- NewPermitted = Thread->Permissions.Inheritable;
- PERMISSION_AND(NewPermitted, FileInheritable);
- PERMISSION_AND(FilePermitted, Thread->Permissions.Limit);
- PERMISSION_OR(NewPermitted, FilePermitted);
- Thread->Permissions.Permitted = NewPermitted;
- if (FileEffective != FALSE) {
- Thread->Permissions.Effective = NewPermitted;
- } else {
- Thread->Permissions.Effective = PERMISSION_SET_EMPTY;
- }
- return;
- }
- KSTATUS
- PspCopyThreadCredentials (
- PKTHREAD NewThread,
- PKTHREAD ThreadToCopy
- )
- /*++
- Routine Description:
- This routine copies the credentials of a thread onto a new yet-to-be-run
- thread.
- Arguments:
- NewThread - Supplies a pointer to the new thread to initialize.
- ThreadToCopy - Supplies a pointer to the thread to copy identity and
- permissions from.
- Return Value:
- STATUS_SUCCESS on success.
- STATUS_INSUFFICIENT_RESOURCES if an allocation failed.
- --*/
- {
- UINTN AllocationSize;
- PSUPPLEMENTARY_GROUPS Block;
- UINTN Count;
- PSUPPLEMENTARY_GROUPS NewBlock;
- KSTATUS Status;
- //
- // Just copy the identity, permissions, and limits straight over.
- //
- RtlCopyMemory(&(NewThread->Identity),
- &(ThreadToCopy->Identity),
- sizeof(THREAD_IDENTITY));
- RtlCopyMemory(&(NewThread->Permissions),
- &(ThreadToCopy->Permissions),
- sizeof(THREAD_PERMISSIONS));
- RtlCopyMemory(&(NewThread->Limits),
- &(ThreadToCopy->Limits),
- sizeof(NewThread->Limits));
- //
- // Count up the old thread supplementary group count so it can be allocated
- // in a single block.
- //
- Count = 0;
- Block = ThreadToCopy->SupplementaryGroups;
- while (Block != NULL) {
- Count += Block->Count;
- Block = Block->Next;
- }
- if (Count != 0) {
- Count = ALIGN_RANGE_UP(Count, SUPPLEMENTARY_GROUP_MIN);
- AllocationSize = sizeof(SUPPLEMENTARY_GROUPS) +
- (Count * sizeof(GROUP_ID));
- NewBlock = MmAllocatePagedPool(AllocationSize, PS_GROUP_ALLOCATION_TAG);
- if (NewBlock == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CopyThreadCredentialsEnd;
- }
- NewBlock->Capacity = Count;
- NewBlock->Count = 0;
- NewBlock->Groups = (PGROUP_ID)(NewBlock + 1);
- NewBlock->Next = NULL;
- //
- // Now copy all the blocks over into the new biggy block.
- //
- Block = ThreadToCopy->SupplementaryGroups;
- Count = 0;
- while (Block != NULL) {
- if (Block->Count != 0) {
- RtlCopyMemory(&(NewBlock->Groups[Count]),
- Block->Groups,
- Block->Count * sizeof(GROUP_ID));
- Count += Block->Count;
- }
- Block = Block->Next;
- }
- ASSERT(Count <= NewBlock->Capacity);
- NewBlock->Count = Count;
- NewThread->SupplementaryGroups = NewBlock;
- }
- Status = STATUS_SUCCESS;
- CopyThreadCredentialsEnd:
- return Status;
- }
- VOID
- PspDestroyCredentials (
- PKTHREAD Thread
- )
- /*++
- Routine Description:
- This routine destroys credentials associated with a dying thread.
- Arguments:
- Thread - Supplies a pointer to the thread being terminated.
- Return Value:
- None.
- --*/
- {
- PSUPPLEMENTARY_GROUPS Groups;
- PSUPPLEMENTARY_GROUPS Next;
- Groups = Thread->SupplementaryGroups;
- Thread->SupplementaryGroups = NULL;
- while (Groups != NULL) {
- Next = Groups->Next;
- MmFreePagedPool(Groups);
- Groups = Next;
- }
- return;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- KSTATUS
- PspSetThreadIdentity (
- ULONG FieldsToSet,
- PTHREAD_IDENTITY Identity
- )
- /*++
- Routine Description:
- This routine gets or sets the current thread's identity.
- Arguments:
- FieldsToSet - Supplies the bitmask of fields to set. Supply 0 to simply get
- the current thread identity. See the THREAD_IDENTITY_FIELD_*
- definitions.
- Identity - Supplies a pointer that on input contains the new identity to
- set. Only the fields specified in the fields to set mask will be
- examined. On output, contains the complete new thread identity
- information.
- Return Value:
- STATUS_SUCCESS on success.
- STATUS_PERMISSION_DENIED if the caller does not have appropriate
- permissions (set user ID and/or set group ID permissions).
- --*/
- {
- PTHREAD_IDENTITY CurrentIdentity;
- BOOL Match;
- KSTATUS Status;
- PKTHREAD Thread;
- BOOL WasRoot;
- Thread = KeGetCurrentThread();
- CurrentIdentity = &(Thread->Identity);
- if (FieldsToSet == 0) {
- Status = STATUS_SUCCESS;
- goto SetThreadIdentityEnd;
- }
- //
- // Before making any changes, ensure the caller isn't overstepping
- // permissions. If changing a user ID, a caller without the set user ID
- // permission can set any user ID to any of its existing user IDs.
- //
- Status = STATUS_SUCCESS;
- if ((FieldsToSet & THREAD_IDENTITY_FIELDS_USER) != 0) {
- Status = PsCheckPermission(PERMISSION_SET_USER_ID);
- if (!KSUCCESS(Status)) {
- Status = STATUS_SUCCESS;
- if ((FieldsToSet & THREAD_IDENTITY_FIELD_REAL_USER_ID) != 0) {
- Match = MATCHES_IDENTITY_USER(Identity->RealUserId,
- CurrentIdentity);
- if (Match == FALSE) {
- Status = STATUS_PERMISSION_DENIED;
- }
- }
- if ((FieldsToSet & THREAD_IDENTITY_FIELD_EFFECTIVE_USER_ID) != 0) {
- Match = MATCHES_IDENTITY_USER(Identity->EffectiveUserId,
- CurrentIdentity);
- if (Match == FALSE) {
- Status = STATUS_PERMISSION_DENIED;
- }
- }
- if ((FieldsToSet & THREAD_IDENTITY_FIELD_SAVED_USER_ID) != 0) {
- Match = MATCHES_IDENTITY_USER(Identity->SavedUserId,
- CurrentIdentity);
- if (Match == FALSE) {
- Status = STATUS_PERMISSION_DENIED;
- }
- }
- }
- if (!KSUCCESS(Status)) {
- goto SetThreadIdentityEnd;
- }
- }
- //
- // Check permissions on the group IDs being set. If the special permission
- // isn't there, then the caller can set a group ID to one of its existing
- // group IDs.
- //
- if ((FieldsToSet & THREAD_IDENTITY_FIELDS_GROUP) != 0) {
- Status = PsCheckPermission(PERMISSION_SET_GROUP_ID);
- if (!KSUCCESS(Status)) {
- Status = STATUS_SUCCESS;
- if ((FieldsToSet & THREAD_IDENTITY_FIELD_REAL_GROUP_ID) != 0) {
- Match = MATCHES_IDENTITY_GROUP(Identity->RealGroupId,
- CurrentIdentity);
- if (Match == FALSE) {
- Status = STATUS_PERMISSION_DENIED;
- }
- }
- if ((FieldsToSet & THREAD_IDENTITY_FIELD_EFFECTIVE_GROUP_ID) != 0) {
- Match = MATCHES_IDENTITY_GROUP(Identity->EffectiveGroupId,
- CurrentIdentity);
- if (Match == FALSE) {
- Status = STATUS_PERMISSION_DENIED;
- }
- }
- if ((FieldsToSet & THREAD_IDENTITY_FIELD_SAVED_GROUP_ID) != 0) {
- Match = MATCHES_IDENTITY_GROUP(Identity->SavedGroupId,
- CurrentIdentity);
- if (Match == FALSE) {
- Status = STATUS_PERMISSION_DENIED;
- }
- }
- }
- if (!KSUCCESS(Status)) {
- goto SetThreadIdentityEnd;
- }
- }
- //
- // Determine if any of the original user IDs were root.
- //
- WasRoot = FALSE;
- if ((CurrentIdentity->RealUserId == USER_ID_ROOT) ||
- (CurrentIdentity->EffectiveUserId == USER_ID_ROOT) ||
- (CurrentIdentity->SavedUserId == USER_ID_ROOT)) {
- WasRoot = TRUE;
- }
- //
- // The permissions all check out, write the new IDs.
- //
- if ((FieldsToSet & THREAD_IDENTITY_FIELD_REAL_USER_ID) != 0) {
- CurrentIdentity->RealUserId = Identity->RealUserId;
- }
- if ((FieldsToSet & THREAD_IDENTITY_FIELD_EFFECTIVE_USER_ID) != 0) {
- //
- // Unless the "no fixup" flag is set, modify the permissions if the
- // effective user ID is moving to or from the traditional root user.
- //
- if ((Thread->Permissions.Behavior &
- PERMISSION_BEHAVIOR_NO_SETUID_FIXUP) == 0) {
- //
- // If the effective user ID goes from zero to non-zero, clear all
- // effective permissions.
- //
- if ((CurrentIdentity->EffectiveUserId == USER_ID_ROOT) &&
- (Identity->EffectiveUserId != USER_ID_ROOT)) {
- Thread->Permissions.Effective = PERMISSION_SET_EMPTY;
- }
- //
- // If the effective user ID goes from non-zero to zero, then copy
- // the permitted permissions to the effective permissions.
- //
- if ((CurrentIdentity->EffectiveUserId != USER_ID_ROOT) &&
- (Identity->EffectiveUserId == USER_ID_ROOT)) {
- Thread->Permissions.Effective = Thread->Permissions.Permitted;
- }
- }
- CurrentIdentity->EffectiveUserId = Identity->EffectiveUserId;
- }
- if ((FieldsToSet & THREAD_IDENTITY_FIELD_SAVED_USER_ID) != 0) {
- CurrentIdentity->SavedUserId = Identity->SavedUserId;
- }
- //
- // If at least one of the real, effective, or saved user IDs was zero and
- // all three are now non-zero, then all permissions are cleared from the
- // permitted and effective sets.
- //
- if ((WasRoot != FALSE) &&
- (CurrentIdentity->RealUserId != USER_ID_ROOT) &&
- (CurrentIdentity->EffectiveUserId != USER_ID_ROOT) &&
- (CurrentIdentity->SavedUserId != USER_ID_ROOT)) {
- if ((Thread->Permissions.Behavior &
- PERMISSION_BEHAVIOR_KEEP_PERMISSIONS) == 0) {
- Thread->Permissions.Permitted = PERMISSION_SET_EMPTY;
- Thread->Permissions.Effective = PERMISSION_SET_EMPTY;
- }
- }
- if ((FieldsToSet & THREAD_IDENTITY_FIELD_REAL_GROUP_ID) != 0) {
- CurrentIdentity->RealGroupId = Identity->RealGroupId;
- }
- if ((FieldsToSet & THREAD_IDENTITY_FIELD_EFFECTIVE_GROUP_ID) != 0) {
- CurrentIdentity->EffectiveGroupId = Identity->EffectiveGroupId;
- }
- if ((FieldsToSet & THREAD_IDENTITY_FIELD_SAVED_GROUP_ID) != 0) {
- CurrentIdentity->SavedGroupId = Identity->SavedGroupId;
- }
- SetThreadIdentityEnd:
- RtlCopyMemory(Identity, CurrentIdentity, sizeof(THREAD_IDENTITY));
- return Status;
- }
- KSTATUS
- PspSetThreadPermissions (
- ULONG FieldsToSet,
- PTHREAD_PERMISSIONS Permissions
- )
- /*++
- Routine Description:
- This routine gets or sets the current thread's permission masks.
- Arguments:
- FieldsToSet - Supplies the bitmask of fields to set. Supply 0 to simply get
- the current thread identity. See the THREAD_PERMISSION_FIELD_*
- definitions.
- Permissions - Supplies a pointer that on input contains the new
- permissions to set. Only the fields specified in the fields to set
- mask will be examined. On output, contains the complete new thread
- permission masks.
- Return Value:
- STATUS_SUCCESS on success.
- STATUS_PERMISSION_DENIED if the caller does not have appropriate
- permissions (potentially the permission to add permissions).
- --*/
- {
- PTHREAD_PERMISSIONS CurrentPermissions;
- ULONG DifferentBits;
- PERMISSION_SET IllegalBits;
- PERMISSION_SET InheritablePlusLimit;
- PERMISSION_SET InheritablePlusPermitted;
- ULONG SameMask;
- KSTATUS Status;
- PKTHREAD Thread;
- Thread = KeGetCurrentThread();
- CurrentPermissions = &(Thread->Permissions);
- if (FieldsToSet == 0) {
- Status = STATUS_SUCCESS;
- goto SetThreadPermissionsEnd;
- }
- //
- // If the thread does not have permission to set more permissions, then
- // additional rules apply.
- //
- Status = PsCheckPermission(PERMISSION_SET_PERMISSIONS);
- if (!KSUCCESS(Status)) {
- Status = STATUS_SUCCESS;
- //
- // The "set permissions" permission is required to change the behavior
- // mask and the limit set.
- //
- if ((FieldsToSet &
- (THREAD_PERMISSION_FIELD_BEHAVIOR |
- THREAD_PERMISSION_FIELD_LIMIT)) != 0) {
- Status = STATUS_PERMISSION_DENIED;
- }
- //
- // The new inheritable mask must only have permissions from the
- // inheritable and permitted sets.
- //
- if ((FieldsToSet & THREAD_PERMISSION_FIELD_INHERITABLE) != 0) {
- InheritablePlusPermitted = CurrentPermissions->Inheritable;
- PERMISSION_OR(InheritablePlusPermitted,
- CurrentPermissions->Permitted);
- IllegalBits = Permissions->Inheritable;
- PERMISSION_REMOVE_SET(IllegalBits, InheritablePlusPermitted);
- if (!PERMISSION_IS_EMPTY(IllegalBits)) {
- Status = STATUS_PERMISSION_DENIED;
- }
- }
- }
- //
- // Bits can never be added to the limit set.
- //
- if ((FieldsToSet & THREAD_PERMISSION_FIELD_LIMIT) != 0) {
- IllegalBits = Permissions->Limit;
- PERMISSION_REMOVE_SET(IllegalBits, CurrentPermissions->Limit);
- if (!PERMISSION_IS_EMPTY(IllegalBits)) {
- Status = STATUS_PERMISSION_DENIED;
- }
- }
- //
- // The lock bits are like fuses, once they're blown they can no longer
- // be changed. For each lock bit that is set, if either the lock bit
- // or the bit itself is different, then fail.
- //
- if ((FieldsToSet & THREAD_PERMISSION_FIELD_BEHAVIOR) != 0) {
- if ((Permissions->Behavior & ~PERMISSION_BEHAVIOR_VALID_MASK) != 0) {
- Status = STATUS_INVALID_PARAMETER;
- }
- SameMask = 0;
- if ((CurrentPermissions->Behavior &
- PERMISSION_BEHAVIOR_KEEP_PERMISSIONS_LOCKED) != 0) {
- SameMask |= PERMISSION_BEHAVIOR_KEEP_PERMISSIONS_LOCKED |
- PERMISSION_BEHAVIOR_KEEP_PERMISSIONS;
- }
- if ((CurrentPermissions->Behavior &
- PERMISSION_BEHAVIOR_NO_SETUID_FIXUP_LOCKED) != 0) {
- SameMask |= PERMISSION_BEHAVIOR_NO_SETUID_FIXUP_LOCKED |
- PERMISSION_BEHAVIOR_NO_SETUID_FIXUP;
- }
- if ((CurrentPermissions->Behavior &
- PERMISSION_BEHAVIOR_NO_ROOT_LOCKED) != 0) {
- SameMask |= PERMISSION_BEHAVIOR_NO_ROOT_LOCKED |
- PERMISSION_BEHAVIOR_NO_ROOT;
- }
- //
- // If any of the bits that are required to be the same are actually
- // different, then the request is denied.
- //
- DifferentBits = Permissions->Behavior ^ CurrentPermissions->Behavior;
- if ((DifferentBits & SameMask) != 0) {
- Status = STATUS_PERMISSION_DENIED;
- }
- }
- //
- // The new inheritable set must be a subset of the existing inheritable
- // set plus the limit.
- //
- if ((FieldsToSet & THREAD_PERMISSION_FIELD_INHERITABLE) != 0) {
- InheritablePlusLimit = CurrentPermissions->Inheritable;
- PERMISSION_OR(InheritablePlusLimit, CurrentPermissions->Limit);
- IllegalBits = Permissions->Inheritable;
- PERMISSION_REMOVE_SET(IllegalBits, InheritablePlusLimit);
- if (!PERMISSION_IS_EMPTY(IllegalBits)) {
- Status = STATUS_PERMISSION_DENIED;
- }
- }
- //
- // Bits cannot be added to the permitted set.
- //
- if ((FieldsToSet & THREAD_PERMISSION_FIELD_PERMITTED) != 0) {
- IllegalBits = Permissions->Permitted;
- PERMISSION_REMOVE_SET(IllegalBits, CurrentPermissions->Permitted);
- if (!PERMISSION_IS_EMPTY(IllegalBits)) {
- Status = STATUS_PERMISSION_DENIED;
- }
- }
- //
- // The effective set is limited to the permitted set.
- //
- if ((FieldsToSet & THREAD_PERMISSION_FIELD_EFFECTIVE) != 0) {
- IllegalBits = Permissions->Effective;
- PERMISSION_REMOVE_SET(IllegalBits, CurrentPermissions->Permitted);
- if (!PERMISSION_IS_EMPTY(IllegalBits)) {
- Status = STATUS_PERMISSION_DENIED;
- }
- }
- //
- // If any of those conditions tripped a failure, don't change any settings.
- //
- if (!KSUCCESS(Status)) {
- goto SetThreadPermissionsEnd;
- }
- //
- // All the checks passed, set the desired fields.
- //
- if ((FieldsToSet & THREAD_PERMISSION_FIELD_BEHAVIOR) != 0) {
- CurrentPermissions->Behavior = Permissions->Behavior;
- }
- if ((FieldsToSet & THREAD_PERMISSION_FIELD_LIMIT) != 0) {
- CurrentPermissions->Limit = Permissions->Limit;
- }
- if ((FieldsToSet & THREAD_PERMISSION_FIELD_INHERITABLE) != 0) {
- CurrentPermissions->Inheritable = Permissions->Inheritable;
- }
- if ((FieldsToSet & THREAD_PERMISSION_FIELD_PERMITTED) != 0) {
- CurrentPermissions->Permitted = Permissions->Permitted;
- }
- if ((FieldsToSet & THREAD_PERMISSION_FIELD_EFFECTIVE) != 0) {
- CurrentPermissions->Effective = Permissions->Effective;
- }
- Status = STATUS_SUCCESS;
- SetThreadPermissionsEnd:
- RtlCopyMemory(Permissions, CurrentPermissions, sizeof(THREAD_PERMISSIONS));
- return Status;
- }
|