mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 10:04:49 +00:00
[NTOS:SE] Reorganize the security manager component
The current state of Security manager's code is kind of a mess. Mainly, there's code scattered around places where they shouldn't belong and token implementation (token.c) is already of a bloat in itself as it is. The file has over 6k lines and it's subject to grow exponentially with improvements, features, whatever that is. With that being said, the token implementation code in the kernel will be split accordingly and rest of the code moved to appropriate places. The new layout will look as follows (excluding the already existing files): - client.c (Client security implementation code) - objtype.c (Object type list implementation code -- more code related to object types will be put here when I'm going to implement object type access checks in the future) - subject.c (Subject security context support) The token implementation in the kernel will be split in 4 distinct files as shown: - token.c (Base token support routines) - tokenlif.c (Life management of a token object -- that is Duplication, Creation and Filtering) - tokencls.c (Token Query/Set Information Classes support) - tokenadj.c (Token privileges/groups adjusting support) In addition to that, tidy up the internal header and reorganize it as well.
This commit is contained in:
parent
96eacfc352
commit
9a2c62b544
12 changed files with 5934 additions and 5669 deletions
|
@ -1,5 +1,16 @@
|
|||
/*
|
||||
* PROJECT: ReactOS Kernel
|
||||
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
||||
* PURPOSE: Internal header for the Security Manager
|
||||
* COPYRIGHT: Copyright Eric Kohl
|
||||
* Copyright 2022 George Bișoc <george.bisoc@reactos.org>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// Internal ACE type structures
|
||||
//
|
||||
typedef struct _KNOWN_ACE
|
||||
{
|
||||
ACE_HEADER Header;
|
||||
|
@ -24,6 +35,9 @@ typedef struct _KNOWN_COMPOUND_ACE
|
|||
ULONG SidStart;
|
||||
} KNOWN_COMPOUND_ACE, *PKNOWN_COMPOUND_ACE;
|
||||
|
||||
//
|
||||
// Access Check Rights
|
||||
//
|
||||
typedef struct _ACCESS_CHECK_RIGHTS
|
||||
{
|
||||
ACCESS_MASK RemainingAccessRights;
|
||||
|
@ -37,6 +51,9 @@ typedef enum _ACCESS_CHECK_RIGHT_TYPE
|
|||
AccessCheckRegular
|
||||
} ACCESS_CHECK_RIGHT_TYPE;
|
||||
|
||||
//
|
||||
// Token Audit Policy Information structure
|
||||
//
|
||||
typedef struct _TOKEN_AUDIT_POLICY_INFORMATION
|
||||
{
|
||||
ULONG PolicyCount;
|
||||
|
@ -47,10 +64,16 @@ typedef struct _TOKEN_AUDIT_POLICY_INFORMATION
|
|||
} Policies[1];
|
||||
} TOKEN_AUDIT_POLICY_INFORMATION, *PTOKEN_AUDIT_POLICY_INFORMATION;
|
||||
|
||||
//
|
||||
// Token creation method defines (for debugging purposes)
|
||||
//
|
||||
#define TOKEN_CREATE_METHOD 0xCUL
|
||||
#define TOKEN_DUPLICATE_METHOD 0xDUL
|
||||
#define TOKEN_FILTER_METHOD 0xFUL
|
||||
|
||||
//
|
||||
// Security descriptor internal helpers
|
||||
//
|
||||
FORCEINLINE
|
||||
PSID
|
||||
SepGetGroupFromDescriptor(
|
||||
|
@ -137,14 +160,18 @@ SepGetSaclFromDescriptor(
|
|||
|
||||
#ifndef RTL_H
|
||||
|
||||
/* SID Authorities */
|
||||
//
|
||||
// SID Authorities
|
||||
//
|
||||
extern SID_IDENTIFIER_AUTHORITY SeNullSidAuthority;
|
||||
extern SID_IDENTIFIER_AUTHORITY SeWorldSidAuthority;
|
||||
extern SID_IDENTIFIER_AUTHORITY SeLocalSidAuthority;
|
||||
extern SID_IDENTIFIER_AUTHORITY SeCreatorSidAuthority;
|
||||
extern SID_IDENTIFIER_AUTHORITY SeNtSidAuthority;
|
||||
|
||||
/* SIDs */
|
||||
//
|
||||
// SIDs
|
||||
//
|
||||
extern PSID SeNullSid;
|
||||
extern PSID SeWorldSid;
|
||||
extern PSID SeLocalSid;
|
||||
|
@ -177,7 +204,9 @@ extern PSID SeAnonymousLogonSid;
|
|||
extern PSID SeLocalServiceSid;
|
||||
extern PSID SeNetworkServiceSid;
|
||||
|
||||
/* Privileges */
|
||||
//
|
||||
// Privileges
|
||||
//
|
||||
extern const LUID SeCreateTokenPrivilege;
|
||||
extern const LUID SeAssignPrimaryTokenPrivilege;
|
||||
extern const LUID SeLockMemoryPrivilege;
|
||||
|
@ -213,14 +242,18 @@ extern const LUID SeIncreaseWorkingSetPrivilege;
|
|||
extern const LUID SeTimeZonePrivilege;
|
||||
extern const LUID SeCreateSymbolicLinkPrivilege;
|
||||
|
||||
/* DACLs */
|
||||
//
|
||||
// DACLs
|
||||
//
|
||||
extern PACL SePublicDefaultUnrestrictedDacl;
|
||||
extern PACL SePublicOpenDacl;
|
||||
extern PACL SePublicOpenUnrestrictedDacl;
|
||||
extern PACL SeUnrestrictedDacl;
|
||||
extern PACL SeSystemAnonymousLogonDacl;
|
||||
|
||||
/* SDs */
|
||||
//
|
||||
// SDs
|
||||
//
|
||||
extern PSECURITY_DESCRIPTOR SePublicDefaultSd;
|
||||
extern PSECURITY_DESCRIPTOR SePublicDefaultUnrestrictedSd;
|
||||
extern PSECURITY_DESCRIPTOR SePublicOpenSd;
|
||||
|
@ -229,11 +262,16 @@ extern PSECURITY_DESCRIPTOR SeSystemDefaultSd;
|
|||
extern PSECURITY_DESCRIPTOR SeUnrestrictedSd;
|
||||
extern PSECURITY_DESCRIPTOR SeSystemAnonymousLogonSd;
|
||||
|
||||
/* Anonymous Logon Tokens */
|
||||
//
|
||||
// Anonymous Logon Tokens
|
||||
//
|
||||
extern PTOKEN SeAnonymousLogonToken;
|
||||
extern PTOKEN SeAnonymousLogonTokenNoEveryone;
|
||||
|
||||
|
||||
//
|
||||
// Token lock management macros
|
||||
//
|
||||
#define SepAcquireTokenLockExclusive(Token) \
|
||||
{ \
|
||||
KeEnterCriticalRegion(); \
|
||||
|
@ -254,128 +292,6 @@ extern PTOKEN SeAnonymousLogonTokenNoEveryone;
|
|||
//
|
||||
// Token Functions
|
||||
//
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SepTokenIsOwner(
|
||||
_In_ PACCESS_TOKEN _Token,
|
||||
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||||
_In_ BOOLEAN TokenLocked);
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SepSidInToken(
|
||||
_In_ PACCESS_TOKEN _Token,
|
||||
_In_ PSID Sid);
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SepSidInTokenEx(
|
||||
_In_ PACCESS_TOKEN _Token,
|
||||
_In_ PSID PrincipalSelfSid,
|
||||
_In_ PSID _Sid,
|
||||
_In_ BOOLEAN Deny,
|
||||
_In_ BOOLEAN Restricted);
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SeTokenCanImpersonate(
|
||||
_In_ PTOKEN ProcessToken,
|
||||
_In_ PTOKEN TokenToImpersonate,
|
||||
_In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel);
|
||||
|
||||
/* Functions */
|
||||
CODE_SEG("INIT")
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SeInitSystem(VOID);
|
||||
|
||||
CODE_SEG("INIT")
|
||||
VOID
|
||||
NTAPI
|
||||
SepInitPrivileges(VOID);
|
||||
|
||||
CODE_SEG("INIT")
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SepInitSecurityIDs(VOID);
|
||||
|
||||
CODE_SEG("INIT")
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SepInitDACLs(VOID);
|
||||
|
||||
CODE_SEG("INIT")
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SepInitSDs(VOID);
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SeRmInitPhase0(VOID);
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SeRmInitPhase1(VOID);
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
SeDeassignPrimaryToken(
|
||||
_Inout_ PEPROCESS Process);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeSubProcessToken(
|
||||
_In_ PTOKEN Parent,
|
||||
_Out_ PTOKEN *Token,
|
||||
_In_ BOOLEAN InUse,
|
||||
_In_ ULONG SessionId);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeInitializeProcessAuditName(
|
||||
_In_ PFILE_OBJECT FileObject,
|
||||
_In_ BOOLEAN DoAudit,
|
||||
_Out_ POBJECT_NAME_INFORMATION *AuditInfo);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeCreateAccessStateEx(
|
||||
_In_ PETHREAD Thread,
|
||||
_In_ PEPROCESS Process,
|
||||
_In_ OUT PACCESS_STATE AccessState,
|
||||
_In_ PAUX_ACCESS_DATA AuxData,
|
||||
_In_ ACCESS_MASK Access,
|
||||
_In_ PGENERIC_MAPPING GenericMapping);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeIsTokenChild(
|
||||
_In_ PTOKEN Token,
|
||||
_Out_ PBOOLEAN IsChild);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeIsTokenSibling(
|
||||
_In_ PTOKEN Token,
|
||||
_Out_ PBOOLEAN IsSibling);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SepCreateImpersonationTokenDacl(
|
||||
_In_ PTOKEN Token,
|
||||
_In_ PTOKEN PrimaryToken,
|
||||
_Out_ PACL* Dacl);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SepRmInsertLogonSessionIntoToken(
|
||||
_Inout_ PTOKEN Token);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SepRmRemoveLogonSessionFromToken(
|
||||
_Inout_ PTOKEN Token);
|
||||
|
||||
CODE_SEG("INIT")
|
||||
VOID
|
||||
NTAPI
|
||||
|
@ -394,20 +310,123 @@ CODE_SEG("INIT")
|
|||
PTOKEN
|
||||
SepCreateSystemAnonymousLogonTokenNoEveryone(VOID);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SepDuplicateToken(
|
||||
_In_ PTOKEN Token,
|
||||
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
|
||||
_In_ BOOLEAN EffectiveOnly,
|
||||
_In_ TOKEN_TYPE TokenType,
|
||||
_In_ SECURITY_IMPERSONATION_LEVEL Level,
|
||||
_In_ KPROCESSOR_MODE PreviousMode,
|
||||
_Out_ PTOKEN* NewAccessToken);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SepCreateToken(
|
||||
_Out_ PHANDLE TokenHandle,
|
||||
_In_ KPROCESSOR_MODE PreviousMode,
|
||||
_In_ ACCESS_MASK DesiredAccess,
|
||||
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
|
||||
_In_ TOKEN_TYPE TokenType,
|
||||
_In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
|
||||
_In_ PLUID AuthenticationId,
|
||||
_In_ PLARGE_INTEGER ExpirationTime,
|
||||
_In_ PSID_AND_ATTRIBUTES User,
|
||||
_In_ ULONG GroupCount,
|
||||
_In_ PSID_AND_ATTRIBUTES Groups,
|
||||
_In_ ULONG GroupsLength,
|
||||
_In_ ULONG PrivilegeCount,
|
||||
_In_ PLUID_AND_ATTRIBUTES Privileges,
|
||||
_In_opt_ PSID Owner,
|
||||
_In_ PSID PrimaryGroup,
|
||||
_In_opt_ PACL DefaultDacl,
|
||||
_In_ PTOKEN_SOURCE TokenSource,
|
||||
_In_ BOOLEAN SystemToken);
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SeDetailedAuditingWithToken(
|
||||
_In_ PTOKEN Token);
|
||||
SepTokenIsOwner(
|
||||
_In_ PACCESS_TOKEN _Token,
|
||||
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||||
_In_ BOOLEAN TokenLocked);
|
||||
|
||||
NTSTATUS
|
||||
SepCreateTokenLock(
|
||||
_Inout_ PTOKEN Token);
|
||||
|
||||
VOID
|
||||
SepDeleteTokenLock(
|
||||
_Inout_ PTOKEN Token);
|
||||
|
||||
VOID
|
||||
SepUpdatePrivilegeFlagsToken(
|
||||
_Inout_ PTOKEN Token);
|
||||
|
||||
NTSTATUS
|
||||
SepFindPrimaryGroupAndDefaultOwner(
|
||||
_In_ PTOKEN Token,
|
||||
_In_ PSID PrimaryGroup,
|
||||
_In_opt_ PSID DefaultOwner,
|
||||
_Out_opt_ PULONG PrimaryGroupIndex,
|
||||
_Out_opt_ PULONG DefaultOwnerIndex);
|
||||
|
||||
VOID
|
||||
SepUpdateSinglePrivilegeFlagToken(
|
||||
_Inout_ PTOKEN Token,
|
||||
_In_ ULONG Index);
|
||||
|
||||
VOID
|
||||
SepUpdatePrivilegeFlagsToken(
|
||||
_Inout_ PTOKEN Token);
|
||||
|
||||
VOID
|
||||
SepRemovePrivilegeToken(
|
||||
_Inout_ PTOKEN Token,
|
||||
_In_ ULONG Index);
|
||||
|
||||
VOID
|
||||
SepRemoveUserGroupToken(
|
||||
_Inout_ PTOKEN Token,
|
||||
_In_ ULONG Index);
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SeTokenCanImpersonate(
|
||||
_In_ PTOKEN ProcessToken,
|
||||
_In_ PTOKEN TokenToImpersonate,
|
||||
_In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel);
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
SeAuditProcessExit(
|
||||
_In_ PEPROCESS Process);
|
||||
SeGetTokenControlInformation(
|
||||
_In_ PACCESS_TOKEN _Token,
|
||||
_Out_ PTOKEN_CONTROL TokenControl);
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
SeAuditProcessCreate(
|
||||
_In_ PEPROCESS Process);
|
||||
SeDeassignPrimaryToken(
|
||||
_Inout_ PEPROCESS Process);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeSubProcessToken(
|
||||
_In_ PTOKEN Parent,
|
||||
_Out_ PTOKEN *Token,
|
||||
_In_ BOOLEAN InUse,
|
||||
_In_ ULONG SessionId);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeIsTokenChild(
|
||||
_In_ PTOKEN Token,
|
||||
_Out_ PBOOLEAN IsChild);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeIsTokenSibling(
|
||||
_In_ PTOKEN Token,
|
||||
_Out_ PBOOLEAN IsSibling);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
|
@ -416,32 +435,58 @@ SeExchangePrimaryToken(
|
|||
_In_ PACCESS_TOKEN NewAccessToken,
|
||||
_Out_ PACCESS_TOKEN* OldAccessToken);
|
||||
|
||||
VOID
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeCaptureSubjectContextEx(
|
||||
_In_ PETHREAD Thread,
|
||||
_In_ PEPROCESS Process,
|
||||
_Out_ PSECURITY_SUBJECT_CONTEXT SubjectContext);
|
||||
SeCopyClientToken(
|
||||
_In_ PACCESS_TOKEN Token,
|
||||
_In_ SECURITY_IMPERSONATION_LEVEL Level,
|
||||
_In_ KPROCESSOR_MODE PreviousMode,
|
||||
_Out_ PACCESS_TOKEN* NewToken);
|
||||
|
||||
ULONG
|
||||
RtlLengthSidAndAttributes(
|
||||
_In_ ULONG Count,
|
||||
_In_ PSID_AND_ATTRIBUTES Src);
|
||||
|
||||
//
|
||||
// Security Manager (SeMgr) functions
|
||||
//
|
||||
CODE_SEG("INIT")
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SeInitSystem(VOID);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeCaptureLuidAndAttributesArray(
|
||||
_In_ PLUID_AND_ATTRIBUTES Src,
|
||||
_In_ ULONG PrivilegeCount,
|
||||
_In_ KPROCESSOR_MODE PreviousMode,
|
||||
_In_ PLUID_AND_ATTRIBUTES AllocatedMem,
|
||||
_In_ ULONG AllocatedLength,
|
||||
SeDefaultObjectMethod(
|
||||
_In_ PVOID Object,
|
||||
_In_ SECURITY_OPERATION_CODE OperationType,
|
||||
_In_ PSECURITY_INFORMATION SecurityInformation,
|
||||
_Inout_opt_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||||
_Inout_opt_ PULONG ReturnLength,
|
||||
_Inout_opt_ PSECURITY_DESCRIPTOR *OldSecurityDescriptor,
|
||||
_In_ POOL_TYPE PoolType,
|
||||
_In_ BOOLEAN CaptureIfKernel,
|
||||
_Out_ PLUID_AND_ATTRIBUTES* Dest,
|
||||
_Inout_ PULONG Length);
|
||||
_In_ PGENERIC_MAPPING GenericMapping);
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
SeReleaseLuidAndAttributesArray(
|
||||
_In_ PLUID_AND_ATTRIBUTES Privilege,
|
||||
_In_ KPROCESSOR_MODE PreviousMode,
|
||||
_In_ BOOLEAN CaptureIfKernel);
|
||||
SeQuerySecurityAccessMask(
|
||||
_In_ SECURITY_INFORMATION SecurityInformation,
|
||||
_Out_ PACCESS_MASK DesiredAccess);
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
SeSetSecurityAccessMask(
|
||||
_In_ SECURITY_INFORMATION SecurityInformation,
|
||||
_Out_ PACCESS_MASK DesiredAccess);
|
||||
|
||||
//
|
||||
// Privilege functions
|
||||
//
|
||||
CODE_SEG("INIT")
|
||||
VOID
|
||||
NTAPI
|
||||
SepInitPrivileges(VOID);
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
|
@ -462,6 +507,12 @@ SePrivilegePolicyCheck(
|
|||
_Out_opt_ PPRIVILEGE_SET *OutPrivilegeSet,
|
||||
_In_ KPROCESSOR_MODE PreviousMode);
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SeCheckAuditPrivilege(
|
||||
_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,
|
||||
_In_ KPROCESSOR_MODE PreviousMode);
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SeCheckPrivilegedObject(
|
||||
|
@ -472,32 +523,32 @@ SeCheckPrivilegedObject(
|
|||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SepDuplicateToken(
|
||||
_In_ PTOKEN Token,
|
||||
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
|
||||
_In_ BOOLEAN EffectiveOnly,
|
||||
_In_ TOKEN_TYPE TokenType,
|
||||
_In_ SECURITY_IMPERSONATION_LEVEL Level,
|
||||
SeCaptureLuidAndAttributesArray(
|
||||
_In_ PLUID_AND_ATTRIBUTES Src,
|
||||
_In_ ULONG PrivilegeCount,
|
||||
_In_ KPROCESSOR_MODE PreviousMode,
|
||||
_Out_ PTOKEN* NewAccessToken);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SepCaptureSecurityQualityOfService(
|
||||
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
|
||||
_In_ KPROCESSOR_MODE AccessMode,
|
||||
_In_ PLUID_AND_ATTRIBUTES AllocatedMem,
|
||||
_In_ ULONG AllocatedLength,
|
||||
_In_ POOL_TYPE PoolType,
|
||||
_In_ BOOLEAN CaptureIfKernel,
|
||||
_Out_ PSECURITY_QUALITY_OF_SERVICE *CapturedSecurityQualityOfService,
|
||||
_Out_ PBOOLEAN Present);
|
||||
_Out_ PLUID_AND_ATTRIBUTES* Dest,
|
||||
_Inout_ PULONG Length);
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
SepReleaseSecurityQualityOfService(
|
||||
_In_opt_ PSECURITY_QUALITY_OF_SERVICE CapturedSecurityQualityOfService,
|
||||
_In_ KPROCESSOR_MODE AccessMode,
|
||||
SeReleaseLuidAndAttributesArray(
|
||||
_In_ PLUID_AND_ATTRIBUTES Privilege,
|
||||
_In_ KPROCESSOR_MODE PreviousMode,
|
||||
_In_ BOOLEAN CaptureIfKernel);
|
||||
|
||||
//
|
||||
// SID functions
|
||||
//
|
||||
CODE_SEG("INIT")
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SepInitSecurityIDs(VOID);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SepCaptureSid(
|
||||
|
@ -514,6 +565,21 @@ SepReleaseSid(
|
|||
_In_ KPROCESSOR_MODE AccessMode,
|
||||
_In_ BOOLEAN CaptureIfKernel);
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SepSidInToken(
|
||||
_In_ PACCESS_TOKEN _Token,
|
||||
_In_ PSID Sid);
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SepSidInTokenEx(
|
||||
_In_ PACCESS_TOKEN _Token,
|
||||
_In_ PSID PrincipalSelfSid,
|
||||
_In_ PSID _Sid,
|
||||
_In_ BOOLEAN Deny,
|
||||
_In_ BOOLEAN Restricted);
|
||||
|
||||
PSID
|
||||
NTAPI
|
||||
SepGetSidFromAce(
|
||||
|
@ -540,11 +606,20 @@ SeReleaseSidAndAttributesArray(
|
|||
_In_ KPROCESSOR_MODE AccessMode,
|
||||
_In_ BOOLEAN CaptureIfKernel);
|
||||
|
||||
//
|
||||
// ACL functions
|
||||
//
|
||||
CODE_SEG("INIT")
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SepInitDACLs(VOID);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeComputeQuotaInformationSize(
|
||||
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||||
_Out_ PULONG QuotaInfoSize);
|
||||
SepCreateImpersonationTokenDacl(
|
||||
_In_ PTOKEN Token,
|
||||
_In_ PTOKEN PrimaryToken,
|
||||
_Out_ PACL* Dacl);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
|
@ -588,17 +663,13 @@ SepSelectAcl(
|
|||
_In_ BOOLEAN IsDirectoryObject,
|
||||
_In_ PGENERIC_MAPPING GenericMapping);
|
||||
|
||||
NTSTATUS
|
||||
//
|
||||
// SD functions
|
||||
//
|
||||
CODE_SEG("INIT")
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SeDefaultObjectMethod(
|
||||
_In_ PVOID Object,
|
||||
_In_ SECURITY_OPERATION_CODE OperationType,
|
||||
_In_ PSECURITY_INFORMATION SecurityInformation,
|
||||
_Inout_opt_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||||
_Inout_opt_ PULONG ReturnLength,
|
||||
_Inout_opt_ PSECURITY_DESCRIPTOR *OldSecurityDescriptor,
|
||||
_In_ POOL_TYPE PoolType,
|
||||
_In_ PGENERIC_MAPPING GenericMapping);
|
||||
SepInitSDs(VOID);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
|
@ -609,54 +680,30 @@ SeSetWorldSecurityDescriptor(
|
|||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeCopyClientToken(
|
||||
_In_ PACCESS_TOKEN Token,
|
||||
_In_ SECURITY_IMPERSONATION_LEVEL Level,
|
||||
_In_ KPROCESSOR_MODE PreviousMode,
|
||||
_Out_ PACCESS_TOKEN* NewToken);
|
||||
SeComputeQuotaInformationSize(
|
||||
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||||
_Out_ PULONG QuotaInfoSize);
|
||||
|
||||
//
|
||||
// Security Reference Monitor (SeRm) functions
|
||||
//
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SeRmInitPhase0(VOID);
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SeRmInitPhase1(VOID);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SepRegQueryHelper(
|
||||
_In_ PCWSTR KeyName,
|
||||
_In_ PCWSTR ValueName,
|
||||
_In_ ULONG ValueType,
|
||||
_In_ ULONG DataLength,
|
||||
_Out_ PVOID ValueData);
|
||||
SepRmInsertLogonSessionIntoToken(
|
||||
_Inout_ PTOKEN Token);
|
||||
|
||||
VOID
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeQuerySecurityAccessMask(
|
||||
_In_ SECURITY_INFORMATION SecurityInformation,
|
||||
_Out_ PACCESS_MASK DesiredAccess);
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
SeSetSecurityAccessMask(
|
||||
_In_ SECURITY_INFORMATION SecurityInformation,
|
||||
_Out_ PACCESS_MASK DesiredAccess);
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SeFastTraverseCheck(
|
||||
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||||
_In_ PACCESS_STATE AccessState,
|
||||
_In_ ACCESS_MASK DesiredAccess,
|
||||
_In_ KPROCESSOR_MODE AccessMode);
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SeCheckAuditPrivilege(
|
||||
_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,
|
||||
_In_ KPROCESSOR_MODE PreviousMode);
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
SePrivilegedServiceAuditAlarm(
|
||||
_In_opt_ PUNICODE_STRING ServiceName,
|
||||
_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,
|
||||
_In_ PPRIVILEGE_SET PrivilegeSet,
|
||||
_In_ BOOLEAN AccessGranted);
|
||||
SepRmRemoveLogonSessionFromToken(
|
||||
_Inout_ PTOKEN Token);
|
||||
|
||||
NTSTATUS
|
||||
SepRmReferenceLogonSession(
|
||||
|
@ -666,12 +713,123 @@ NTSTATUS
|
|||
SepRmDereferenceLogonSession(
|
||||
_Inout_ PLUID LogonLuid);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SepRegQueryHelper(
|
||||
_In_ PCWSTR KeyName,
|
||||
_In_ PCWSTR ValueName,
|
||||
_In_ ULONG ValueType,
|
||||
_In_ ULONG DataLength,
|
||||
_Out_ PVOID ValueData);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeGetLogonIdDeviceMap(
|
||||
_In_ PLUID LogonId,
|
||||
_Out_ PDEVICE_MAP *DeviceMap);
|
||||
|
||||
//
|
||||
// Audit functions
|
||||
//
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeInitializeProcessAuditName(
|
||||
_In_ PFILE_OBJECT FileObject,
|
||||
_In_ BOOLEAN DoAudit,
|
||||
_Out_ POBJECT_NAME_INFORMATION *AuditInfo);
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SeDetailedAuditingWithToken(
|
||||
_In_ PTOKEN Token);
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
SeAuditProcessExit(
|
||||
_In_ PEPROCESS Process);
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
SeAuditProcessCreate(
|
||||
_In_ PEPROCESS Process);
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
SePrivilegedServiceAuditAlarm(
|
||||
_In_opt_ PUNICODE_STRING ServiceName,
|
||||
_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,
|
||||
_In_ PPRIVILEGE_SET PrivilegeSet,
|
||||
_In_ BOOLEAN AccessGranted);
|
||||
|
||||
//
|
||||
// Subject functions
|
||||
//
|
||||
VOID
|
||||
NTAPI
|
||||
SeCaptureSubjectContextEx(
|
||||
_In_ PETHREAD Thread,
|
||||
_In_ PEPROCESS Process,
|
||||
_Out_ PSECURITY_SUBJECT_CONTEXT SubjectContext);
|
||||
|
||||
//
|
||||
// Security Quality of Service (SQoS) functions
|
||||
//
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SepCaptureSecurityQualityOfService(
|
||||
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
|
||||
_In_ KPROCESSOR_MODE AccessMode,
|
||||
_In_ POOL_TYPE PoolType,
|
||||
_In_ BOOLEAN CaptureIfKernel,
|
||||
_Out_ PSECURITY_QUALITY_OF_SERVICE *CapturedSecurityQualityOfService,
|
||||
_Out_ PBOOLEAN Present);
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
SepReleaseSecurityQualityOfService(
|
||||
_In_opt_ PSECURITY_QUALITY_OF_SERVICE CapturedSecurityQualityOfService,
|
||||
_In_ KPROCESSOR_MODE AccessMode,
|
||||
_In_ BOOLEAN CaptureIfKernel);
|
||||
|
||||
//
|
||||
// Object type list functions
|
||||
//
|
||||
NTSTATUS
|
||||
SeCaptureObjectTypeList(
|
||||
_In_reads_opt_(ObjectTypeListLength) POBJECT_TYPE_LIST ObjectTypeList,
|
||||
_In_ ULONG ObjectTypeListLength,
|
||||
_In_ KPROCESSOR_MODE PreviousMode,
|
||||
_Out_ POBJECT_TYPE_LIST *CapturedObjectTypeList);
|
||||
|
||||
VOID
|
||||
SeReleaseObjectTypeList(
|
||||
_In_ _Post_invalid_ POBJECT_TYPE_LIST CapturedObjectTypeList,
|
||||
_In_ KPROCESSOR_MODE PreviousMode);
|
||||
|
||||
//
|
||||
// Access state functions
|
||||
//
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeCreateAccessStateEx(
|
||||
_In_ PETHREAD Thread,
|
||||
_In_ PEPROCESS Process,
|
||||
_In_ OUT PACCESS_STATE AccessState,
|
||||
_In_ PAUX_ACCESS_DATA AuxData,
|
||||
_In_ ACCESS_MASK Access,
|
||||
_In_ PGENERIC_MAPPING GenericMapping);
|
||||
|
||||
//
|
||||
// Access check functions
|
||||
//
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SeFastTraverseCheck(
|
||||
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||||
_In_ PACCESS_STATE AccessState,
|
||||
_In_ ACCESS_MASK DesiredAccess,
|
||||
_In_ KPROCESSOR_MODE AccessMode);
|
||||
|
||||
#endif
|
||||
|
||||
/* EOF */
|
||||
|
|
|
@ -275,13 +275,19 @@ list(APPEND SOURCE
|
|||
${REACTOS_SOURCE_DIR}/ntoskrnl/se/accesschk.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/se/acl.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/se/audit.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/se/client.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/se/objtype.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/se/priv.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/se/sd.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/se/semgr.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/se/sid.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/se/sqos.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/se/srm.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/se/subject.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/se/token.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/se/tokenadj.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/se/tokencls.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/se/tokenlif.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/vf/driver.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/wmi/guidobj.c
|
||||
${REACTOS_SOURCE_DIR}/ntoskrnl/wmi/smbios.c
|
||||
|
|
|
@ -11,529 +11,8 @@
|
|||
#define NDEBUG
|
||||
#include <debug.h>
|
||||
|
||||
/* GLOBALS ********************************************************************/
|
||||
|
||||
ERESOURCE SepSubjectContextLock;
|
||||
|
||||
/* PRIVATE FUNCTIONS **********************************************************/
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Checks if a SID is present in a token.
|
||||
*
|
||||
* @param[in] _Token
|
||||
* A valid token object.
|
||||
*
|
||||
* @param[in] PrincipalSelfSid
|
||||
* A principal self SID.
|
||||
*
|
||||
* @param[in] _Sid
|
||||
* A regular SID.
|
||||
*
|
||||
* @param[in] Deny
|
||||
* If set to TRUE, the caller expected that a SID in a token
|
||||
* must be a deny-only SID, that is, access checks are performed
|
||||
* only for deny-only ACEs of the said SID.
|
||||
*
|
||||
* @param[in] Restricted
|
||||
* If set to TRUE, the caller expects that a SID in a token is
|
||||
* restricted (by the general definition, a token is restricted).
|
||||
*
|
||||
* @return
|
||||
* Returns TRUE if the specified SID in the call is present in the token,
|
||||
* FALSE otherwise.
|
||||
*/
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SepSidInTokenEx(
|
||||
_In_ PACCESS_TOKEN _Token,
|
||||
_In_ PSID PrincipalSelfSid,
|
||||
_In_ PSID _Sid,
|
||||
_In_ BOOLEAN Deny,
|
||||
_In_ BOOLEAN Restricted)
|
||||
{
|
||||
ULONG SidIndex;
|
||||
PTOKEN Token = (PTOKEN)_Token;
|
||||
PISID TokenSid, Sid = (PISID)_Sid;
|
||||
PSID_AND_ATTRIBUTES SidAndAttributes;
|
||||
ULONG SidCount, SidLength;
|
||||
USHORT SidMetadata;
|
||||
PAGED_CODE();
|
||||
|
||||
/* Check if a principal SID was given, and this is our current SID already */
|
||||
if ((PrincipalSelfSid) && (RtlEqualSid(SePrincipalSelfSid, Sid)))
|
||||
{
|
||||
/* Just use the principal SID in this case */
|
||||
Sid = PrincipalSelfSid;
|
||||
}
|
||||
|
||||
/* Check if this is a restricted token or not */
|
||||
if (Restricted)
|
||||
{
|
||||
/* Use the restricted SIDs and count */
|
||||
SidAndAttributes = Token->RestrictedSids;
|
||||
SidCount = Token->RestrictedSidCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Use the normal SIDs and count */
|
||||
SidAndAttributes = Token->UserAndGroups;
|
||||
SidCount = Token->UserAndGroupCount;
|
||||
}
|
||||
|
||||
/* Do checks here by hand instead of the usual 4 function calls */
|
||||
SidLength = FIELD_OFFSET(SID,
|
||||
SubAuthority[Sid->SubAuthorityCount]);
|
||||
SidMetadata = *(PUSHORT)&Sid->Revision;
|
||||
|
||||
/* Loop every SID */
|
||||
for (SidIndex = 0; SidIndex < SidCount; SidIndex++)
|
||||
{
|
||||
TokenSid = (PISID)SidAndAttributes->Sid;
|
||||
#if SE_SID_DEBUG
|
||||
UNICODE_STRING sidString;
|
||||
RtlConvertSidToUnicodeString(&sidString, TokenSid, TRUE);
|
||||
DPRINT1("SID in Token: %wZ\n", &sidString);
|
||||
RtlFreeUnicodeString(&sidString);
|
||||
#endif
|
||||
/* Check if the SID metadata matches */
|
||||
if (*(PUSHORT)&TokenSid->Revision == SidMetadata)
|
||||
{
|
||||
/* Check if the SID data matches */
|
||||
if (RtlEqualMemory(Sid, TokenSid, SidLength))
|
||||
{
|
||||
/*
|
||||
* Check if the group is enabled, or used for deny only.
|
||||
* Otherwise we have to check if this is the first user.
|
||||
* We understand that by looking if this SID is not
|
||||
* restricted, this is the first element we are iterating
|
||||
* and that it doesn't have SE_GROUP_USE_FOR_DENY_ONLY
|
||||
* attribute.
|
||||
*/
|
||||
if ((!Restricted && (SidIndex == 0) && !(SidAndAttributes->Attributes & SE_GROUP_USE_FOR_DENY_ONLY)) ||
|
||||
(SidAndAttributes->Attributes & SE_GROUP_ENABLED) ||
|
||||
((Deny) && (SidAndAttributes->Attributes & SE_GROUP_USE_FOR_DENY_ONLY)))
|
||||
{
|
||||
/* SID is present */
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* SID is not present */
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Move to the next SID */
|
||||
SidAndAttributes++;
|
||||
}
|
||||
|
||||
/* SID is not present */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Checks if a SID is present in a token.
|
||||
*
|
||||
* @param[in] _Token
|
||||
* A valid token object.
|
||||
*
|
||||
* @param[in] _Sid
|
||||
* A regular SID.
|
||||
*
|
||||
* @return
|
||||
* Returns TRUE if the specified SID in the call is present in the token,
|
||||
* FALSE otherwise.
|
||||
*/
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SepSidInToken(
|
||||
_In_ PACCESS_TOKEN _Token,
|
||||
_In_ PSID Sid)
|
||||
{
|
||||
/* Call extended API */
|
||||
return SepSidInTokenEx(_Token, NULL, Sid, FALSE, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Checks if a token belongs to the main user, being the owner.
|
||||
*
|
||||
* @param[in] _Token
|
||||
* A valid token object.
|
||||
*
|
||||
* @param[in] SecurityDescriptor
|
||||
* A security descriptor where the owner is to be found.
|
||||
*
|
||||
* @param[in] TokenLocked
|
||||
* If set to TRUE, the token has been already locked and there's
|
||||
* no need to lock it again. Otherwise the function will acquire
|
||||
* the lock.
|
||||
*
|
||||
* @return
|
||||
* Returns TRUE if the token belongs to a owner, FALSE otherwise.
|
||||
*/
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SepTokenIsOwner(
|
||||
_In_ PACCESS_TOKEN _Token,
|
||||
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||||
_In_ BOOLEAN TokenLocked)
|
||||
{
|
||||
PSID Sid;
|
||||
BOOLEAN Result;
|
||||
PTOKEN Token = _Token;
|
||||
|
||||
/* Get the owner SID */
|
||||
Sid = SepGetOwnerFromDescriptor(SecurityDescriptor);
|
||||
ASSERT(Sid != NULL);
|
||||
|
||||
/* Lock the token if needed */
|
||||
if (!TokenLocked) SepAcquireTokenLockShared(Token);
|
||||
|
||||
/* Check if the owner SID is found, handling restricted case as well */
|
||||
Result = SepSidInToken(Token, Sid);
|
||||
if ((Result) && (Token->TokenFlags & TOKEN_IS_RESTRICTED))
|
||||
{
|
||||
Result = SepSidInTokenEx(Token, NULL, Sid, FALSE, TRUE);
|
||||
}
|
||||
|
||||
/* Release the lock if we had acquired it */
|
||||
if (!TokenLocked) SepReleaseTokenLock(Token);
|
||||
|
||||
/* Return the result */
|
||||
return Result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Retrieves token control information.
|
||||
*
|
||||
* @param[in] _Token
|
||||
* A valid token object.
|
||||
*
|
||||
* @param[out] SecurityDescriptor
|
||||
* The returned token control information.
|
||||
*
|
||||
* @return
|
||||
* Nothing.
|
||||
*/
|
||||
VOID
|
||||
NTAPI
|
||||
SeGetTokenControlInformation(
|
||||
_In_ PACCESS_TOKEN _Token,
|
||||
_Out_ PTOKEN_CONTROL TokenControl)
|
||||
{
|
||||
PTOKEN Token = _Token;
|
||||
PAGED_CODE();
|
||||
|
||||
/* Capture the main fields */
|
||||
TokenControl->AuthenticationId = Token->AuthenticationId;
|
||||
TokenControl->TokenId = Token->TokenId;
|
||||
TokenControl->TokenSource = Token->TokenSource;
|
||||
|
||||
/* Lock the token */
|
||||
SepAcquireTokenLockShared(Token);
|
||||
|
||||
/* Capture the modified ID */
|
||||
TokenControl->ModifiedId = Token->ModifiedId;
|
||||
|
||||
/* Unlock it */
|
||||
SepReleaseTokenLock(Token);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Creates a client security context based upon an access token.
|
||||
*
|
||||
* @param[in] Token
|
||||
* A valid token object.
|
||||
*
|
||||
* @param[in] ClientSecurityQos
|
||||
* The Quality of Service (QoS) of a client security context.
|
||||
*
|
||||
* @param[in] ServerIsRemote
|
||||
* If the client is a remote server (TRUE), the function will retrieve the
|
||||
* control information of an access token, that is, we're doing delegation
|
||||
* and that the server isn't local.
|
||||
*
|
||||
* @param[in] TokenType
|
||||
* Type of token.
|
||||
*
|
||||
* @param[in] ThreadEffectiveOnly
|
||||
* If set to TRUE, the client wants that the current thread wants to modify
|
||||
* (enable or disable) privileges and groups.
|
||||
*
|
||||
* @param[in] ImpersonationLevel
|
||||
* Security impersonation level filled in the QoS context.
|
||||
*
|
||||
* @param[out] ClientContext
|
||||
* The returned security client context.
|
||||
*
|
||||
* @return
|
||||
* Returns STATUS_SUCCESS if client security creation has completed successfully.
|
||||
* STATUS_INVALID_PARAMETER is returned if one or more of the parameters are bogus.
|
||||
* STATUS_BAD_IMPERSONATION_LEVEL is returned if the current impersonation level
|
||||
* within QoS context doesn't meet with the conditions required. A failure
|
||||
* NTSTATUS code is returned otherwise.
|
||||
*/
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SepCreateClientSecurity(
|
||||
_In_ PACCESS_TOKEN Token,
|
||||
_In_ PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos,
|
||||
_In_ BOOLEAN ServerIsRemote,
|
||||
_In_ TOKEN_TYPE TokenType,
|
||||
_In_ BOOLEAN ThreadEffectiveOnly,
|
||||
_In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
|
||||
_Out_ PSECURITY_CLIENT_CONTEXT ClientContext)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
PACCESS_TOKEN NewToken;
|
||||
PAGED_CODE();
|
||||
|
||||
/* Check for bogus impersonation level */
|
||||
if (!VALID_IMPERSONATION_LEVEL(ClientSecurityQos->ImpersonationLevel))
|
||||
{
|
||||
/* Fail the call */
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Check what kind of token this is */
|
||||
if (TokenType != TokenImpersonation)
|
||||
{
|
||||
/* On a primary token, if we do direct access, copy the flag from the QOS */
|
||||
ClientContext->DirectAccessEffectiveOnly = ClientSecurityQos->EffectiveOnly;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This is an impersonation token, is the level ok? */
|
||||
if (ClientSecurityQos->ImpersonationLevel > ImpersonationLevel)
|
||||
{
|
||||
/* Nope, fail */
|
||||
return STATUS_BAD_IMPERSONATION_LEVEL;
|
||||
}
|
||||
|
||||
/* Is the level too low, or are we doing something other than delegation remotely */
|
||||
if ((ImpersonationLevel == SecurityAnonymous) ||
|
||||
(ImpersonationLevel == SecurityIdentification) ||
|
||||
((ServerIsRemote) && (ImpersonationLevel != SecurityDelegation)))
|
||||
{
|
||||
/* Fail the call */
|
||||
return STATUS_BAD_IMPERSONATION_LEVEL;
|
||||
}
|
||||
|
||||
/* Pick either the thread setting or the QOS setting */
|
||||
ClientContext->DirectAccessEffectiveOnly =
|
||||
((ThreadEffectiveOnly) || (ClientSecurityQos->EffectiveOnly)) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
/* Is this static tracking */
|
||||
if (ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING)
|
||||
{
|
||||
/* Do not use direct access and make a copy */
|
||||
ClientContext->DirectlyAccessClientToken = FALSE;
|
||||
Status = SeCopyClientToken(Token,
|
||||
ClientSecurityQos->ImpersonationLevel,
|
||||
KernelMode,
|
||||
&NewToken);
|
||||
if (!NT_SUCCESS(Status))
|
||||
return Status;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Use direct access and check if this is local */
|
||||
ClientContext->DirectlyAccessClientToken = TRUE;
|
||||
if (ServerIsRemote)
|
||||
{
|
||||
/* We are doing delegation, so make a copy of the control data */
|
||||
SeGetTokenControlInformation(Token,
|
||||
&ClientContext->ClientTokenControl);
|
||||
}
|
||||
|
||||
/* Keep the same token */
|
||||
NewToken = Token;
|
||||
}
|
||||
|
||||
/* Fill out the context and return success */
|
||||
ClientContext->SecurityQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
|
||||
ClientContext->SecurityQos.ImpersonationLevel = ClientSecurityQos->ImpersonationLevel;
|
||||
ClientContext->SecurityQos.ContextTrackingMode = ClientSecurityQos->ContextTrackingMode;
|
||||
ClientContext->SecurityQos.EffectiveOnly = ClientSecurityQos->EffectiveOnly;
|
||||
ClientContext->ServerIsRemote = ServerIsRemote;
|
||||
ClientContext->ClientToken = NewToken;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* PUBLIC FUNCTIONS ***********************************************************/
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* An extended function that captures the security subject context based upon
|
||||
* the specified thread and process.
|
||||
*
|
||||
* @param[in] Thread
|
||||
* A thread where the calling thread's token is to be referenced for
|
||||
* the security context.
|
||||
*
|
||||
* @param[in] Process
|
||||
* A process where the main process' token is to be referenced for
|
||||
* the security context.
|
||||
*
|
||||
* @param[out] SubjectContext
|
||||
* The returned security subject context.
|
||||
*
|
||||
* @return
|
||||
* Nothing.
|
||||
*/
|
||||
VOID
|
||||
NTAPI
|
||||
SeCaptureSubjectContextEx(
|
||||
_In_ PETHREAD Thread,
|
||||
_In_ PEPROCESS Process,
|
||||
_Out_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
|
||||
{
|
||||
BOOLEAN CopyOnOpen, EffectiveOnly;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
/* Save the unique ID */
|
||||
SubjectContext->ProcessAuditId = Process->UniqueProcessId;
|
||||
|
||||
/* Check if we have a thread */
|
||||
if (!Thread)
|
||||
{
|
||||
/* We don't, so no token */
|
||||
SubjectContext->ClientToken = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Get the impersonation token */
|
||||
SubjectContext->ClientToken = PsReferenceImpersonationToken(Thread,
|
||||
&CopyOnOpen,
|
||||
&EffectiveOnly,
|
||||
&SubjectContext->ImpersonationLevel);
|
||||
}
|
||||
|
||||
/* Get the primary token */
|
||||
SubjectContext->PrimaryToken = PsReferencePrimaryToken(Process);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Captures the security subject context of the calling thread and calling
|
||||
* process.
|
||||
*
|
||||
* @param[out] SubjectContext
|
||||
* The returned security subject context.
|
||||
*
|
||||
* @return
|
||||
* Nothing.
|
||||
*/
|
||||
VOID
|
||||
NTAPI
|
||||
SeCaptureSubjectContext(
|
||||
_Out_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
|
||||
{
|
||||
/* Call the extended API */
|
||||
SeCaptureSubjectContextEx(PsGetCurrentThread(),
|
||||
PsGetCurrentProcess(),
|
||||
SubjectContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Locks both the referenced primary and client access tokens of a
|
||||
* security subject context.
|
||||
*
|
||||
* @param[in] SubjectContext
|
||||
* A valid security context with both referenced tokens.
|
||||
*
|
||||
* @return
|
||||
* Nothing.
|
||||
*/
|
||||
VOID
|
||||
NTAPI
|
||||
SeLockSubjectContext(
|
||||
_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
|
||||
{
|
||||
PTOKEN PrimaryToken, ClientToken;
|
||||
PAGED_CODE();
|
||||
|
||||
/* Read both tokens */
|
||||
PrimaryToken = SubjectContext->PrimaryToken;
|
||||
ClientToken = SubjectContext->ClientToken;
|
||||
|
||||
/* Always lock the primary */
|
||||
SepAcquireTokenLockShared(PrimaryToken);
|
||||
|
||||
/* Lock the impersonation one if it's there */
|
||||
if (!ClientToken) return;
|
||||
SepAcquireTokenLockShared(ClientToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Unlocks both the referenced primary and client access tokens of a
|
||||
* security subject context.
|
||||
*
|
||||
* @param[in] SubjectContext
|
||||
* A valid security context with both referenced tokens.
|
||||
*
|
||||
* @return
|
||||
* Nothing.
|
||||
*/
|
||||
VOID
|
||||
NTAPI
|
||||
SeUnlockSubjectContext(
|
||||
_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
|
||||
{
|
||||
PTOKEN PrimaryToken, ClientToken;
|
||||
PAGED_CODE();
|
||||
|
||||
/* Read both tokens */
|
||||
PrimaryToken = SubjectContext->PrimaryToken;
|
||||
ClientToken = SubjectContext->ClientToken;
|
||||
|
||||
/* Unlock the impersonation one if it's there */
|
||||
if (ClientToken)
|
||||
{
|
||||
SepReleaseTokenLock(ClientToken);
|
||||
}
|
||||
|
||||
/* Always unlock the primary one */
|
||||
SepReleaseTokenLock(PrimaryToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Releases both the primary and client tokens of a security
|
||||
* subject context.
|
||||
*
|
||||
* @param[in] SubjectContext
|
||||
* The captured security context.
|
||||
*
|
||||
* @return
|
||||
* Nothing.
|
||||
*/
|
||||
VOID
|
||||
NTAPI
|
||||
SeReleaseSubjectContext(
|
||||
_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
/* Drop reference on the primary */
|
||||
ObFastDereferenceObject(&PsGetCurrentProcess()->Token, SubjectContext->PrimaryToken);
|
||||
SubjectContext->PrimaryToken = NULL;
|
||||
|
||||
/* Drop reference on the impersonation, if there was one */
|
||||
PsDereferenceImpersonationToken(SubjectContext->ClientToken);
|
||||
SubjectContext->ClientToken = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* An extended function that creates an access state.
|
||||
|
@ -721,194 +200,4 @@ SeSetAccessStateGenericMapping(
|
|||
((PAUX_ACCESS_DATA)AccessState->AuxData)->GenericMapping = *GenericMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Creates a client security context.
|
||||
*
|
||||
* @param[in] Thread
|
||||
* Thread object of the client where impersonation has to begin.
|
||||
*
|
||||
* @param[in] Qos
|
||||
* Quality of service to specify what kind of impersonation to be done.
|
||||
*
|
||||
* @param[in] RemoteClient
|
||||
* If set to TRUE, the client that we're going to impersonate is remote.
|
||||
*
|
||||
* @param[out] ClientContext
|
||||
* The returned security client context.
|
||||
*
|
||||
* @return
|
||||
* See SepCreateClientSecurity.
|
||||
*/
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeCreateClientSecurity(
|
||||
_In_ PETHREAD Thread,
|
||||
_In_ PSECURITY_QUALITY_OF_SERVICE Qos,
|
||||
_In_ BOOLEAN RemoteClient,
|
||||
_Out_ PSECURITY_CLIENT_CONTEXT ClientContext)
|
||||
{
|
||||
TOKEN_TYPE TokenType;
|
||||
BOOLEAN ThreadEffectiveOnly;
|
||||
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
|
||||
PACCESS_TOKEN Token;
|
||||
NTSTATUS Status;
|
||||
PAGED_CODE();
|
||||
|
||||
/* Reference the correct token */
|
||||
Token = PsReferenceEffectiveToken(Thread,
|
||||
&TokenType,
|
||||
&ThreadEffectiveOnly,
|
||||
&ImpersonationLevel);
|
||||
|
||||
/* Create client security from it */
|
||||
Status = SepCreateClientSecurity(Token,
|
||||
Qos,
|
||||
RemoteClient,
|
||||
TokenType,
|
||||
ThreadEffectiveOnly,
|
||||
ImpersonationLevel,
|
||||
ClientContext);
|
||||
|
||||
/* Check if we failed or static tracking was used */
|
||||
if (!(NT_SUCCESS(Status)) || (Qos->ContextTrackingMode == SECURITY_STATIC_TRACKING))
|
||||
{
|
||||
/* Dereference our copy since it's not being used */
|
||||
ObDereferenceObject(Token);
|
||||
}
|
||||
|
||||
/* Return status */
|
||||
return Status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Creates a client security context based upon the captured security
|
||||
* subject context.
|
||||
*
|
||||
* @param[in] SubjectContext
|
||||
* The captured subject context where client security is to be created
|
||||
* from.
|
||||
*
|
||||
* @param[in] ClientSecurityQos
|
||||
* Quality of service to specify what kind of impersonation to be done.
|
||||
*
|
||||
* @param[in] ServerIsRemote
|
||||
* If set to TRUE, the client that we're going to impersonate is remote.
|
||||
*
|
||||
* @param[out] ClientContext
|
||||
* The returned security client context.
|
||||
*
|
||||
* @return
|
||||
* See SepCreateClientSecurity.
|
||||
*/
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeCreateClientSecurityFromSubjectContext(
|
||||
_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,
|
||||
_In_ PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos,
|
||||
_In_ BOOLEAN ServerIsRemote,
|
||||
_Out_ PSECURITY_CLIENT_CONTEXT ClientContext)
|
||||
{
|
||||
PACCESS_TOKEN Token;
|
||||
NTSTATUS Status;
|
||||
PAGED_CODE();
|
||||
|
||||
/* Get the right token and reference it */
|
||||
Token = SeQuerySubjectContextToken(SubjectContext);
|
||||
ObReferenceObject(Token);
|
||||
|
||||
/* Create the context */
|
||||
Status = SepCreateClientSecurity(Token,
|
||||
ClientSecurityQos,
|
||||
ServerIsRemote,
|
||||
SubjectContext->ClientToken ?
|
||||
TokenImpersonation : TokenPrimary,
|
||||
FALSE,
|
||||
SubjectContext->ImpersonationLevel,
|
||||
ClientContext);
|
||||
|
||||
/* Check if we failed or static tracking was used */
|
||||
if (!(NT_SUCCESS(Status)) ||
|
||||
(ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING))
|
||||
{
|
||||
/* Dereference our copy since it's not being used */
|
||||
ObDereferenceObject(Token);
|
||||
}
|
||||
|
||||
/* Return status */
|
||||
return Status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Extended function that impersonates a client.
|
||||
*
|
||||
* @param[in] ClientContext
|
||||
* A valid client context.
|
||||
*
|
||||
* @param[in] ServerThread
|
||||
* The thread where impersonation is to be done.
|
||||
*
|
||||
* @return
|
||||
* STATUS_SUCCESS is returned if the calling thread successfully impersonates
|
||||
* the client. A failure NTSTATUS code is returned otherwise.
|
||||
*/
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeImpersonateClientEx(
|
||||
_In_ PSECURITY_CLIENT_CONTEXT ClientContext,
|
||||
_In_opt_ PETHREAD ServerThread)
|
||||
{
|
||||
BOOLEAN EffectiveOnly;
|
||||
PAGED_CODE();
|
||||
|
||||
/* Check if direct access is requested */
|
||||
if (!ClientContext->DirectlyAccessClientToken)
|
||||
{
|
||||
/* No, so get the flag from QOS */
|
||||
EffectiveOnly = ClientContext->SecurityQos.EffectiveOnly;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Yes, so see if direct access should be effective only */
|
||||
EffectiveOnly = ClientContext->DirectAccessEffectiveOnly;
|
||||
}
|
||||
|
||||
/* Use the current thread if one was not passed */
|
||||
if (!ServerThread) ServerThread = PsGetCurrentThread();
|
||||
|
||||
/* Call the lower layer routine */
|
||||
return PsImpersonateClient(ServerThread,
|
||||
ClientContext->ClientToken,
|
||||
TRUE,
|
||||
EffectiveOnly,
|
||||
ClientContext->SecurityQos.ImpersonationLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Impersonates a client user.
|
||||
*
|
||||
* @param[in] ClientContext
|
||||
* A valid client context.
|
||||
*
|
||||
* @param[in] ServerThread
|
||||
* The thread where impersonation is to be done.
|
||||
* *
|
||||
* @return
|
||||
* Nothing.
|
||||
*/
|
||||
VOID
|
||||
NTAPI
|
||||
SeImpersonateClient(
|
||||
_In_ PSECURITY_CLIENT_CONTEXT ClientContext,
|
||||
_In_opt_ PETHREAD ServerThread)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
/* Call the new API */
|
||||
SeImpersonateClientEx(ClientContext, ServerThread);
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
UNICODE_STRING SeSubsystemName = RTL_CONSTANT_STRING(L"Security");
|
||||
|
||||
/* PRIVATE FUNCTIONS***********************************************************/
|
||||
/* PRIVATE FUNCTIONS ***********************************************************/
|
||||
|
||||
/**
|
||||
* @unimplemented
|
||||
|
@ -411,107 +411,6 @@ SePrivilegedServiceAuditAlarm(
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Captures a list of object types.
|
||||
*
|
||||
* @param[in] ObjectTypeList
|
||||
* An existing list of object types.
|
||||
*
|
||||
* @param[in] ObjectTypeListLength
|
||||
* The length size of the list.
|
||||
*
|
||||
* @param[in] PreviousMode
|
||||
* Processor access level mode.
|
||||
*
|
||||
* @param[out] CapturedObjectTypeList
|
||||
* The captured list of object types.
|
||||
*
|
||||
* @return
|
||||
* Returns STATUS_SUCCESS if the list of object types has been captured
|
||||
* successfully. STATUS_INVALID_PARAMETER is returned if the caller hasn't
|
||||
* supplied a buffer list of object types. STATUS_INSUFFICIENT_RESOURCES
|
||||
* is returned if pool memory allocation for the captured list has failed.
|
||||
*/
|
||||
static
|
||||
NTSTATUS
|
||||
SeCaptureObjectTypeList(
|
||||
_In_reads_opt_(ObjectTypeListLength) POBJECT_TYPE_LIST ObjectTypeList,
|
||||
_In_ ULONG ObjectTypeListLength,
|
||||
_In_ KPROCESSOR_MODE PreviousMode,
|
||||
_Out_ POBJECT_TYPE_LIST *CapturedObjectTypeList)
|
||||
{
|
||||
SIZE_T Size;
|
||||
|
||||
if (PreviousMode == KernelMode)
|
||||
{
|
||||
return STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
if (ObjectTypeListLength == 0)
|
||||
{
|
||||
*CapturedObjectTypeList = NULL;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (ObjectTypeList == NULL)
|
||||
{
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Calculate the list size and check for integer overflow */
|
||||
Size = ObjectTypeListLength * sizeof(OBJECT_TYPE_LIST);
|
||||
if (Size == 0)
|
||||
{
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Allocate a new list */
|
||||
*CapturedObjectTypeList = ExAllocatePoolWithTag(PagedPool, Size, TAG_SEPA);
|
||||
if (*CapturedObjectTypeList == NULL)
|
||||
{
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
_SEH2_TRY
|
||||
{
|
||||
ProbeForRead(ObjectTypeList, Size, sizeof(ULONG));
|
||||
RtlCopyMemory(*CapturedObjectTypeList, ObjectTypeList, Size);
|
||||
}
|
||||
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
ExFreePoolWithTag(*CapturedObjectTypeList, TAG_SEPA);
|
||||
*CapturedObjectTypeList = NULL;
|
||||
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
||||
}
|
||||
_SEH2_END;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Releases a buffer list of object types.
|
||||
*
|
||||
* @param[in] CapturedObjectTypeList
|
||||
* A list of object types to free.
|
||||
*
|
||||
* @param[in] PreviousMode
|
||||
* Processor access level mode.
|
||||
*
|
||||
* @return
|
||||
* Nothing.
|
||||
*/
|
||||
static
|
||||
VOID
|
||||
SeReleaseObjectTypeList(
|
||||
_In_ _Post_invalid_ POBJECT_TYPE_LIST CapturedObjectTypeList,
|
||||
_In_ KPROCESSOR_MODE PreviousMode)
|
||||
{
|
||||
if ((PreviousMode != KernelMode) && (CapturedObjectTypeList != NULL))
|
||||
ExFreePoolWithTag(CapturedObjectTypeList, TAG_SEPA);
|
||||
}
|
||||
|
||||
/**
|
||||
* @unimplemented
|
||||
* @brief
|
||||
|
|
331
ntoskrnl/se/client.c
Normal file
331
ntoskrnl/se/client.c
Normal file
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
* PROJECT: ReactOS Kernel
|
||||
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
||||
* PURPOSE: Security client support routines
|
||||
* COPYRIGHT: Copyright Alex Ionescu <alex@relsoft.net>
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include <ntoskrnl.h>
|
||||
#define NDEBUG
|
||||
#include <debug.h>
|
||||
|
||||
/* PRIVATE FUNCTIONS **********************************************************/
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Creates a client security context based upon an access token.
|
||||
*
|
||||
* @param[in] Token
|
||||
* A valid token object.
|
||||
*
|
||||
* @param[in] ClientSecurityQos
|
||||
* The Quality of Service (QoS) of a client security context.
|
||||
*
|
||||
* @param[in] ServerIsRemote
|
||||
* If the client is a remote server (TRUE), the function will retrieve the
|
||||
* control information of an access token, that is, we're doing delegation
|
||||
* and that the server isn't local.
|
||||
*
|
||||
* @param[in] TokenType
|
||||
* Type of token.
|
||||
*
|
||||
* @param[in] ThreadEffectiveOnly
|
||||
* If set to TRUE, the client wants that the current thread wants to modify
|
||||
* (enable or disable) privileges and groups.
|
||||
*
|
||||
* @param[in] ImpersonationLevel
|
||||
* Security impersonation level filled in the QoS context.
|
||||
*
|
||||
* @param[out] ClientContext
|
||||
* The returned security client context.
|
||||
*
|
||||
* @return
|
||||
* Returns STATUS_SUCCESS if client security creation has completed successfully.
|
||||
* STATUS_INVALID_PARAMETER is returned if one or more of the parameters are bogus.
|
||||
* STATUS_BAD_IMPERSONATION_LEVEL is returned if the current impersonation level
|
||||
* within QoS context doesn't meet with the conditions required. A failure
|
||||
* NTSTATUS code is returned otherwise.
|
||||
*/
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SepCreateClientSecurity(
|
||||
_In_ PACCESS_TOKEN Token,
|
||||
_In_ PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos,
|
||||
_In_ BOOLEAN ServerIsRemote,
|
||||
_In_ TOKEN_TYPE TokenType,
|
||||
_In_ BOOLEAN ThreadEffectiveOnly,
|
||||
_In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
|
||||
_Out_ PSECURITY_CLIENT_CONTEXT ClientContext)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
PACCESS_TOKEN NewToken;
|
||||
PAGED_CODE();
|
||||
|
||||
/* Check for bogus impersonation level */
|
||||
if (!VALID_IMPERSONATION_LEVEL(ClientSecurityQos->ImpersonationLevel))
|
||||
{
|
||||
/* Fail the call */
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Check what kind of token this is */
|
||||
if (TokenType != TokenImpersonation)
|
||||
{
|
||||
/* On a primary token, if we do direct access, copy the flag from the QOS */
|
||||
ClientContext->DirectAccessEffectiveOnly = ClientSecurityQos->EffectiveOnly;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This is an impersonation token, is the level ok? */
|
||||
if (ClientSecurityQos->ImpersonationLevel > ImpersonationLevel)
|
||||
{
|
||||
/* Nope, fail */
|
||||
return STATUS_BAD_IMPERSONATION_LEVEL;
|
||||
}
|
||||
|
||||
/* Is the level too low, or are we doing something other than delegation remotely */
|
||||
if ((ImpersonationLevel == SecurityAnonymous) ||
|
||||
(ImpersonationLevel == SecurityIdentification) ||
|
||||
((ServerIsRemote) && (ImpersonationLevel != SecurityDelegation)))
|
||||
{
|
||||
/* Fail the call */
|
||||
return STATUS_BAD_IMPERSONATION_LEVEL;
|
||||
}
|
||||
|
||||
/* Pick either the thread setting or the QOS setting */
|
||||
ClientContext->DirectAccessEffectiveOnly =
|
||||
((ThreadEffectiveOnly) || (ClientSecurityQos->EffectiveOnly)) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
/* Is this static tracking */
|
||||
if (ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING)
|
||||
{
|
||||
/* Do not use direct access and make a copy */
|
||||
ClientContext->DirectlyAccessClientToken = FALSE;
|
||||
Status = SeCopyClientToken(Token,
|
||||
ClientSecurityQos->ImpersonationLevel,
|
||||
KernelMode,
|
||||
&NewToken);
|
||||
if (!NT_SUCCESS(Status))
|
||||
return Status;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Use direct access and check if this is local */
|
||||
ClientContext->DirectlyAccessClientToken = TRUE;
|
||||
if (ServerIsRemote)
|
||||
{
|
||||
/* We are doing delegation, so make a copy of the control data */
|
||||
SeGetTokenControlInformation(Token,
|
||||
&ClientContext->ClientTokenControl);
|
||||
}
|
||||
|
||||
/* Keep the same token */
|
||||
NewToken = Token;
|
||||
}
|
||||
|
||||
/* Fill out the context and return success */
|
||||
ClientContext->SecurityQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
|
||||
ClientContext->SecurityQos.ImpersonationLevel = ClientSecurityQos->ImpersonationLevel;
|
||||
ClientContext->SecurityQos.ContextTrackingMode = ClientSecurityQos->ContextTrackingMode;
|
||||
ClientContext->SecurityQos.EffectiveOnly = ClientSecurityQos->EffectiveOnly;
|
||||
ClientContext->ServerIsRemote = ServerIsRemote;
|
||||
ClientContext->ClientToken = NewToken;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* PUBLIC FUNCTIONS ***********************************************************/
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Creates a client security context.
|
||||
*
|
||||
* @param[in] Thread
|
||||
* Thread object of the client where impersonation has to begin.
|
||||
*
|
||||
* @param[in] Qos
|
||||
* Quality of service to specify what kind of impersonation to be done.
|
||||
*
|
||||
* @param[in] RemoteClient
|
||||
* If set to TRUE, the client that we're going to impersonate is remote.
|
||||
*
|
||||
* @param[out] ClientContext
|
||||
* The returned security client context.
|
||||
*
|
||||
* @return
|
||||
* See SepCreateClientSecurity.
|
||||
*/
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeCreateClientSecurity(
|
||||
_In_ PETHREAD Thread,
|
||||
_In_ PSECURITY_QUALITY_OF_SERVICE Qos,
|
||||
_In_ BOOLEAN RemoteClient,
|
||||
_Out_ PSECURITY_CLIENT_CONTEXT ClientContext)
|
||||
{
|
||||
TOKEN_TYPE TokenType;
|
||||
BOOLEAN ThreadEffectiveOnly;
|
||||
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
|
||||
PACCESS_TOKEN Token;
|
||||
NTSTATUS Status;
|
||||
PAGED_CODE();
|
||||
|
||||
/* Reference the correct token */
|
||||
Token = PsReferenceEffectiveToken(Thread,
|
||||
&TokenType,
|
||||
&ThreadEffectiveOnly,
|
||||
&ImpersonationLevel);
|
||||
|
||||
/* Create client security from it */
|
||||
Status = SepCreateClientSecurity(Token,
|
||||
Qos,
|
||||
RemoteClient,
|
||||
TokenType,
|
||||
ThreadEffectiveOnly,
|
||||
ImpersonationLevel,
|
||||
ClientContext);
|
||||
|
||||
/* Check if we failed or static tracking was used */
|
||||
if (!(NT_SUCCESS(Status)) || (Qos->ContextTrackingMode == SECURITY_STATIC_TRACKING))
|
||||
{
|
||||
/* Dereference our copy since it's not being used */
|
||||
ObDereferenceObject(Token);
|
||||
}
|
||||
|
||||
/* Return status */
|
||||
return Status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Creates a client security context based upon the captured security
|
||||
* subject context.
|
||||
*
|
||||
* @param[in] SubjectContext
|
||||
* The captured subject context where client security is to be created
|
||||
* from.
|
||||
*
|
||||
* @param[in] ClientSecurityQos
|
||||
* Quality of service to specify what kind of impersonation to be done.
|
||||
*
|
||||
* @param[in] ServerIsRemote
|
||||
* If set to TRUE, the client that we're going to impersonate is remote.
|
||||
*
|
||||
* @param[out] ClientContext
|
||||
* The returned security client context.
|
||||
*
|
||||
* @return
|
||||
* See SepCreateClientSecurity.
|
||||
*/
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeCreateClientSecurityFromSubjectContext(
|
||||
_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,
|
||||
_In_ PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos,
|
||||
_In_ BOOLEAN ServerIsRemote,
|
||||
_Out_ PSECURITY_CLIENT_CONTEXT ClientContext)
|
||||
{
|
||||
PACCESS_TOKEN Token;
|
||||
NTSTATUS Status;
|
||||
PAGED_CODE();
|
||||
|
||||
/* Get the right token and reference it */
|
||||
Token = SeQuerySubjectContextToken(SubjectContext);
|
||||
ObReferenceObject(Token);
|
||||
|
||||
/* Create the context */
|
||||
Status = SepCreateClientSecurity(Token,
|
||||
ClientSecurityQos,
|
||||
ServerIsRemote,
|
||||
SubjectContext->ClientToken ?
|
||||
TokenImpersonation : TokenPrimary,
|
||||
FALSE,
|
||||
SubjectContext->ImpersonationLevel,
|
||||
ClientContext);
|
||||
|
||||
/* Check if we failed or static tracking was used */
|
||||
if (!(NT_SUCCESS(Status)) ||
|
||||
(ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING))
|
||||
{
|
||||
/* Dereference our copy since it's not being used */
|
||||
ObDereferenceObject(Token);
|
||||
}
|
||||
|
||||
/* Return status */
|
||||
return Status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Extended function that impersonates a client.
|
||||
*
|
||||
* @param[in] ClientContext
|
||||
* A valid client context.
|
||||
*
|
||||
* @param[in] ServerThread
|
||||
* The thread where impersonation is to be done.
|
||||
*
|
||||
* @return
|
||||
* STATUS_SUCCESS is returned if the calling thread successfully impersonates
|
||||
* the client. A failure NTSTATUS code is returned otherwise.
|
||||
*/
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
SeImpersonateClientEx(
|
||||
_In_ PSECURITY_CLIENT_CONTEXT ClientContext,
|
||||
_In_opt_ PETHREAD ServerThread)
|
||||
{
|
||||
BOOLEAN EffectiveOnly;
|
||||
PAGED_CODE();
|
||||
|
||||
/* Check if direct access is requested */
|
||||
if (!ClientContext->DirectlyAccessClientToken)
|
||||
{
|
||||
/* No, so get the flag from QOS */
|
||||
EffectiveOnly = ClientContext->SecurityQos.EffectiveOnly;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Yes, so see if direct access should be effective only */
|
||||
EffectiveOnly = ClientContext->DirectAccessEffectiveOnly;
|
||||
}
|
||||
|
||||
/* Use the current thread if one was not passed */
|
||||
if (!ServerThread) ServerThread = PsGetCurrentThread();
|
||||
|
||||
/* Call the lower layer routine */
|
||||
return PsImpersonateClient(ServerThread,
|
||||
ClientContext->ClientToken,
|
||||
TRUE,
|
||||
EffectiveOnly,
|
||||
ClientContext->SecurityQos.ImpersonationLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Impersonates a client user.
|
||||
*
|
||||
* @param[in] ClientContext
|
||||
* A valid client context.
|
||||
*
|
||||
* @param[in] ServerThread
|
||||
* The thread where impersonation is to be done.
|
||||
* *
|
||||
* @return
|
||||
* Nothing.
|
||||
*/
|
||||
VOID
|
||||
NTAPI
|
||||
SeImpersonateClient(
|
||||
_In_ PSECURITY_CLIENT_CONTEXT ClientContext,
|
||||
_In_opt_ PETHREAD ServerThread)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
/* Call the new API */
|
||||
SeImpersonateClientEx(ClientContext, ServerThread);
|
||||
}
|
||||
|
||||
/* EOF */
|
115
ntoskrnl/se/objtype.c
Normal file
115
ntoskrnl/se/objtype.c
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* PROJECT: ReactOS Kernel
|
||||
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
||||
* PURPOSE: Security object type list support routines
|
||||
* COPYRIGHT: Copyright Timo Kreuzer <timo.kreuzer@reactos.org>
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include <ntoskrnl.h>
|
||||
#define NDEBUG
|
||||
#include <debug.h>
|
||||
|
||||
/* PRIVATE FUNCTIONS ***********************************************************/
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Captures a list of object types.
|
||||
*
|
||||
* @param[in] ObjectTypeList
|
||||
* An existing list of object types.
|
||||
*
|
||||
* @param[in] ObjectTypeListLength
|
||||
* The length size of the list.
|
||||
*
|
||||
* @param[in] PreviousMode
|
||||
* Processor access level mode.
|
||||
*
|
||||
* @param[out] CapturedObjectTypeList
|
||||
* The captured list of object types.
|
||||
*
|
||||
* @return
|
||||
* Returns STATUS_SUCCESS if the list of object types has been captured
|
||||
* successfully. STATUS_INVALID_PARAMETER is returned if the caller hasn't
|
||||
* supplied a buffer list of object types. STATUS_INSUFFICIENT_RESOURCES
|
||||
* is returned if pool memory allocation for the captured list has failed.
|
||||
*/
|
||||
NTSTATUS
|
||||
SeCaptureObjectTypeList(
|
||||
_In_reads_opt_(ObjectTypeListLength) POBJECT_TYPE_LIST ObjectTypeList,
|
||||
_In_ ULONG ObjectTypeListLength,
|
||||
_In_ KPROCESSOR_MODE PreviousMode,
|
||||
_Out_ POBJECT_TYPE_LIST *CapturedObjectTypeList)
|
||||
{
|
||||
SIZE_T Size;
|
||||
|
||||
if (PreviousMode == KernelMode)
|
||||
{
|
||||
return STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
if (ObjectTypeListLength == 0)
|
||||
{
|
||||
*CapturedObjectTypeList = NULL;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (ObjectTypeList == NULL)
|
||||
{
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Calculate the list size and check for integer overflow */
|
||||
Size = ObjectTypeListLength * sizeof(OBJECT_TYPE_LIST);
|
||||
if (Size == 0)
|
||||
{
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Allocate a new list */
|
||||
*CapturedObjectTypeList = ExAllocatePoolWithTag(PagedPool, Size, TAG_SEPA);
|
||||
if (*CapturedObjectTypeList == NULL)
|
||||
{
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
_SEH2_TRY
|
||||
{
|
||||
ProbeForRead(ObjectTypeList, Size, sizeof(ULONG));
|
||||
RtlCopyMemory(*CapturedObjectTypeList, ObjectTypeList, Size);
|
||||
}
|
||||
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
ExFreePoolWithTag(*CapturedObjectTypeList, TAG_SEPA);
|
||||
*CapturedObjectTypeList = NULL;
|
||||
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
||||
}
|
||||
_SEH2_END;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Releases a buffer list of object types.
|
||||
*
|
||||
* @param[in] CapturedObjectTypeList
|
||||
* A list of object types to free.
|
||||
*
|
||||
* @param[in] PreviousMode
|
||||
* Processor access level mode.
|
||||
*
|
||||
* @return
|
||||
* Nothing.
|
||||
*/
|
||||
VOID
|
||||
SeReleaseObjectTypeList(
|
||||
_In_ _Post_invalid_ POBJECT_TYPE_LIST CapturedObjectTypeList,
|
||||
_In_ KPROCESSOR_MODE PreviousMode)
|
||||
{
|
||||
if ((PreviousMode != KernelMode) && (CapturedObjectTypeList != NULL))
|
||||
ExFreePoolWithTag(CapturedObjectTypeList, TAG_SEPA);
|
||||
}
|
||||
|
||||
/* EOF */
|
|
@ -412,6 +412,146 @@ SepReleaseSid(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Checks if a SID is present in a token.
|
||||
*
|
||||
* @param[in] _Token
|
||||
* A valid token object.
|
||||
*
|
||||
* @param[in] PrincipalSelfSid
|
||||
* A principal self SID.
|
||||
*
|
||||
* @param[in] _Sid
|
||||
* A regular SID.
|
||||
*
|
||||
* @param[in] Deny
|
||||
* If set to TRUE, the caller expected that a SID in a token
|
||||
* must be a deny-only SID, that is, access checks are performed
|
||||
* only for deny-only ACEs of the said SID.
|
||||
*
|
||||
* @param[in] Restricted
|
||||
* If set to TRUE, the caller expects that a SID in a token is
|
||||
* restricted (by the general definition, a token is restricted).
|
||||
*
|
||||
* @return
|
||||
* Returns TRUE if the specified SID in the call is present in the token,
|
||||
* FALSE otherwise.
|
||||
*/
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SepSidInTokenEx(
|
||||
_In_ PACCESS_TOKEN _Token,
|
||||
_In_ PSID PrincipalSelfSid,
|
||||
_In_ PSID _Sid,
|
||||
_In_ BOOLEAN Deny,
|
||||
_In_ BOOLEAN Restricted)
|
||||
{
|
||||
ULONG SidIndex;
|
||||
PTOKEN Token = (PTOKEN)_Token;
|
||||
PISID TokenSid, Sid = (PISID)_Sid;
|
||||
PSID_AND_ATTRIBUTES SidAndAttributes;
|
||||
ULONG SidCount, SidLength;
|
||||
USHORT SidMetadata;
|
||||
PAGED_CODE();
|
||||
|
||||
/* Check if a principal SID was given, and this is our current SID already */
|
||||
if ((PrincipalSelfSid) && (RtlEqualSid(SePrincipalSelfSid, Sid)))
|
||||
{
|
||||
/* Just use the principal SID in this case */
|
||||
Sid = PrincipalSelfSid;
|
||||
}
|
||||
|
||||
/* Check if this is a restricted token or not */
|
||||
if (Restricted)
|
||||
{
|
||||
/* Use the restricted SIDs and count */
|
||||
SidAndAttributes = Token->RestrictedSids;
|
||||
SidCount = Token->RestrictedSidCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Use the normal SIDs and count */
|
||||
SidAndAttributes = Token->UserAndGroups;
|
||||
SidCount = Token->UserAndGroupCount;
|
||||
}
|
||||
|
||||
/* Do checks here by hand instead of the usual 4 function calls */
|
||||
SidLength = FIELD_OFFSET(SID,
|
||||
SubAuthority[Sid->SubAuthorityCount]);
|
||||
SidMetadata = *(PUSHORT)&Sid->Revision;
|
||||
|
||||
/* Loop every SID */
|
||||
for (SidIndex = 0; SidIndex < SidCount; SidIndex++)
|
||||
{
|
||||
TokenSid = (PISID)SidAndAttributes->Sid;
|
||||
#if SE_SID_DEBUG
|
||||
UNICODE_STRING sidString;
|
||||
RtlConvertSidToUnicodeString(&sidString, TokenSid, TRUE);
|
||||
DPRINT1("SID in Token: %wZ\n", &sidString);
|
||||
RtlFreeUnicodeString(&sidString);
|
||||
#endif
|
||||
/* Check if the SID metadata matches */
|
||||
if (*(PUSHORT)&TokenSid->Revision == SidMetadata)
|
||||
{
|
||||
/* Check if the SID data matches */
|
||||
if (RtlEqualMemory(Sid, TokenSid, SidLength))
|
||||
{
|
||||
/*
|
||||
* Check if the group is enabled, or used for deny only.
|
||||
* Otherwise we have to check if this is the first user.
|
||||
* We understand that by looking if this SID is not
|
||||
* restricted, this is the first element we are iterating
|
||||
* and that it doesn't have SE_GROUP_USE_FOR_DENY_ONLY
|
||||
* attribute.
|
||||
*/
|
||||
if ((!Restricted && (SidIndex == 0) && !(SidAndAttributes->Attributes & SE_GROUP_USE_FOR_DENY_ONLY)) ||
|
||||
(SidAndAttributes->Attributes & SE_GROUP_ENABLED) ||
|
||||
((Deny) && (SidAndAttributes->Attributes & SE_GROUP_USE_FOR_DENY_ONLY)))
|
||||
{
|
||||
/* SID is present */
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* SID is not present */
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Move to the next SID */
|
||||
SidAndAttributes++;
|
||||
}
|
||||
|
||||
/* SID is not present */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Checks if a SID is present in a token.
|
||||
*
|
||||
* @param[in] _Token
|
||||
* A valid token object.
|
||||
*
|
||||
* @param[in] _Sid
|
||||
* A regular SID.
|
||||
*
|
||||
* @return
|
||||
* Returns TRUE if the specified SID in the call is present in the token,
|
||||
* FALSE otherwise.
|
||||
*/
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
SepSidInToken(
|
||||
_In_ PACCESS_TOKEN _Token,
|
||||
_In_ PSID Sid)
|
||||
{
|
||||
/* Call extended API */
|
||||
return SepSidInTokenEx(_Token, NULL, Sid, FALSE, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Captures a security identifier from a
|
||||
|
|
185
ntoskrnl/se/subject.c
Normal file
185
ntoskrnl/se/subject.c
Normal file
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* PROJECT: ReactOS Kernel
|
||||
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
||||
* PURPOSE: Security subject context support routines
|
||||
* COPYRIGHT: Copyright Alex Ionescu <alex@relsoft.net>
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include <ntoskrnl.h>
|
||||
#define NDEBUG
|
||||
#include <debug.h>
|
||||
|
||||
/* GLOBALS ********************************************************************/
|
||||
|
||||
ERESOURCE SepSubjectContextLock;
|
||||
|
||||
/* PUBLIC FUNCTIONS ***********************************************************/
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* An extended function that captures the security subject context based upon
|
||||
* the specified thread and process.
|
||||
*
|
||||
* @param[in] Thread
|
||||
* A thread where the calling thread's token is to be referenced for
|
||||
* the security context.
|
||||
*
|
||||
* @param[in] Process
|
||||
* A process where the main process' token is to be referenced for
|
||||
* the security context.
|
||||
*
|
||||
* @param[out] SubjectContext
|
||||
* The returned security subject context.
|
||||
*
|
||||
* @return
|
||||
* Nothing.
|
||||
*/
|
||||
VOID
|
||||
NTAPI
|
||||
SeCaptureSubjectContextEx(
|
||||
_In_ PETHREAD Thread,
|
||||
_In_ PEPROCESS Process,
|
||||
_Out_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
|
||||
{
|
||||
BOOLEAN CopyOnOpen, EffectiveOnly;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
/* Save the unique ID */
|
||||
SubjectContext->ProcessAuditId = Process->UniqueProcessId;
|
||||
|
||||
/* Check if we have a thread */
|
||||
if (!Thread)
|
||||
{
|
||||
/* We don't, so no token */
|
||||
SubjectContext->ClientToken = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Get the impersonation token */
|
||||
SubjectContext->ClientToken = PsReferenceImpersonationToken(Thread,
|
||||
&CopyOnOpen,
|
||||
&EffectiveOnly,
|
||||
&SubjectContext->ImpersonationLevel);
|
||||
}
|
||||
|
||||
/* Get the primary token */
|
||||
SubjectContext->PrimaryToken = PsReferencePrimaryToken(Process);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Captures the security subject context of the calling thread and calling
|
||||
* process.
|
||||
*
|
||||
* @param[out] SubjectContext
|
||||
* The returned security subject context.
|
||||
*
|
||||
* @return
|
||||
* Nothing.
|
||||
*/
|
||||
VOID
|
||||
NTAPI
|
||||
SeCaptureSubjectContext(
|
||||
_Out_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
|
||||
{
|
||||
/* Call the extended API */
|
||||
SeCaptureSubjectContextEx(PsGetCurrentThread(),
|
||||
PsGetCurrentProcess(),
|
||||
SubjectContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Locks both the referenced primary and client access tokens of a
|
||||
* security subject context.
|
||||
*
|
||||
* @param[in] SubjectContext
|
||||
* A valid security context with both referenced tokens.
|
||||
*
|
||||
* @return
|
||||
* Nothing.
|
||||
*/
|
||||
VOID
|
||||
NTAPI
|
||||
SeLockSubjectContext(
|
||||
_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
|
||||
{
|
||||
PTOKEN PrimaryToken, ClientToken;
|
||||
PAGED_CODE();
|
||||
|
||||
/* Read both tokens */
|
||||
PrimaryToken = SubjectContext->PrimaryToken;
|
||||
ClientToken = SubjectContext->ClientToken;
|
||||
|
||||
/* Always lock the primary */
|
||||
SepAcquireTokenLockShared(PrimaryToken);
|
||||
|
||||
/* Lock the impersonation one if it's there */
|
||||
if (!ClientToken) return;
|
||||
SepAcquireTokenLockShared(ClientToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Unlocks both the referenced primary and client access tokens of a
|
||||
* security subject context.
|
||||
*
|
||||
* @param[in] SubjectContext
|
||||
* A valid security context with both referenced tokens.
|
||||
*
|
||||
* @return
|
||||
* Nothing.
|
||||
*/
|
||||
VOID
|
||||
NTAPI
|
||||
SeUnlockSubjectContext(
|
||||
_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
|
||||
{
|
||||
PTOKEN PrimaryToken, ClientToken;
|
||||
PAGED_CODE();
|
||||
|
||||
/* Read both tokens */
|
||||
PrimaryToken = SubjectContext->PrimaryToken;
|
||||
ClientToken = SubjectContext->ClientToken;
|
||||
|
||||
/* Unlock the impersonation one if it's there */
|
||||
if (ClientToken)
|
||||
{
|
||||
SepReleaseTokenLock(ClientToken);
|
||||
}
|
||||
|
||||
/* Always unlock the primary one */
|
||||
SepReleaseTokenLock(PrimaryToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Releases both the primary and client tokens of a security
|
||||
* subject context.
|
||||
*
|
||||
* @param[in] SubjectContext
|
||||
* The captured security context.
|
||||
*
|
||||
* @return
|
||||
* Nothing.
|
||||
*/
|
||||
VOID
|
||||
NTAPI
|
||||
SeReleaseSubjectContext(
|
||||
_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
/* Drop reference on the primary */
|
||||
ObFastDereferenceObject(&PsGetCurrentProcess()->Token, SubjectContext->PrimaryToken);
|
||||
SubjectContext->PrimaryToken = NULL;
|
||||
|
||||
/* Drop reference on the impersonation, if there was one */
|
||||
PsDereferenceImpersonationToken(SubjectContext->ClientToken);
|
||||
SubjectContext->ClientToken = NULL;
|
||||
}
|
||||
|
||||
/* EOF */
|
4703
ntoskrnl/se/token.c
4703
ntoskrnl/se/token.c
File diff suppressed because it is too large
Load diff
910
ntoskrnl/se/tokenadj.c
Normal file
910
ntoskrnl/se/tokenadj.c
Normal file
|
@ -0,0 +1,910 @@
|
|||
/*
|
||||
* PROJECT: ReactOS Kernel
|
||||
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
||||
* PURPOSE: Access token ajusting Groups/Privileges support routines
|
||||
* COPYRIGHT: Copyright David Welch <welch@cwcom.net>
|
||||
* Copyright 2021-2022 George Bișoc <george.bisoc@reactos.org>
|
||||
*/
|
||||
|
||||
/* INCLUDES *******************************************************************/
|
||||
|
||||
#include <ntoskrnl.h>
|
||||
#define NDEBUG
|
||||
#include <debug.h>
|
||||
|
||||
/* PRIVATE FUNCTIONS *********************************************************/
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Removes a certain amount of privileges of a token based upon the request
|
||||
* by the caller.
|
||||
*
|
||||
* @param[in,out] Token
|
||||
* Token handle where the privileges are about to be modified.
|
||||
*
|
||||
* @param[in] DisableAllPrivileges
|
||||
* If set to TRUE, the function disables all the privileges.
|
||||
*
|
||||
* @param[in] NewState
|
||||
* A new list of privileges that the function will use it accordingly to
|
||||
* either disable or enable the said privileges and change them.
|
||||
*
|
||||
* @param[in] NewStateCount
|
||||
* The new total number count of privileges.
|
||||
*
|
||||
* @param[out] PreviousState
|
||||
* If specified, the function will return the previous state list of privileges.
|
||||
*
|
||||
* @param[in] ApplyChanges
|
||||
* If set to TRUE, the function will immediatelly apply the changes onto the
|
||||
* token's privileges.
|
||||
*
|
||||
* @param[out] ChangedPrivileges
|
||||
* The returned count number of changed privileges.
|
||||
*
|
||||
* @param[out] ChangesMade
|
||||
* If TRUE, the function has made changes to the token's privileges. FALSE
|
||||
* otherwise.
|
||||
*
|
||||
* @return
|
||||
* Returns STATUS_SUCCESS if the function has successfully changed the list
|
||||
* of privileges. STATUS_NOT_ALL_ASSIGNED is returned if not every privilege
|
||||
* has been changed.
|
||||
*/
|
||||
static
|
||||
NTSTATUS
|
||||
SepAdjustPrivileges(
|
||||
_Inout_ PTOKEN Token,
|
||||
_In_ BOOLEAN DisableAllPrivileges,
|
||||
_In_opt_ PLUID_AND_ATTRIBUTES NewState,
|
||||
_In_ ULONG NewStateCount,
|
||||
_Out_opt_ PTOKEN_PRIVILEGES PreviousState,
|
||||
_In_ BOOLEAN ApplyChanges,
|
||||
_Out_ PULONG ChangedPrivileges,
|
||||
_Out_ PBOOLEAN ChangesMade)
|
||||
{
|
||||
ULONG i, j, PrivilegeCount, ChangeCount, NewAttributes;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
/* Count the found privileges and those that need to be changed */
|
||||
PrivilegeCount = 0;
|
||||
ChangeCount = 0;
|
||||
*ChangesMade = FALSE;
|
||||
|
||||
/* Loop all privileges in the token */
|
||||
for (i = 0; i < Token->PrivilegeCount; i++)
|
||||
{
|
||||
/* Shall all of them be disabled? */
|
||||
if (DisableAllPrivileges)
|
||||
{
|
||||
/* The new attributes are the old ones, but disabled */
|
||||
NewAttributes = Token->Privileges[i].Attributes & ~SE_PRIVILEGE_ENABLED;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise loop all provided privileges */
|
||||
for (j = 0; j < NewStateCount; j++)
|
||||
{
|
||||
/* Check if this is the LUID we are looking for */
|
||||
if (RtlEqualLuid(&Token->Privileges[i].Luid, &NewState[j].Luid))
|
||||
{
|
||||
DPRINT("Found privilege\n");
|
||||
|
||||
/* Copy SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_REMOVED */
|
||||
NewAttributes = NewState[j].Attributes;
|
||||
NewAttributes &= (SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_REMOVED);
|
||||
NewAttributes |= Token->Privileges[i].Attributes & ~SE_PRIVILEGE_ENABLED;
|
||||
|
||||
/* Stop looking */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if we didn't find the privilege */
|
||||
if (j == NewStateCount)
|
||||
{
|
||||
/* Continue with the token's next privilege */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* We found a privilege, count it */
|
||||
PrivilegeCount++;
|
||||
|
||||
/* Does the privilege need to be changed? */
|
||||
if (Token->Privileges[i].Attributes != NewAttributes)
|
||||
{
|
||||
/* Does the caller want the old privileges? */
|
||||
if (PreviousState != NULL)
|
||||
{
|
||||
/* Copy the old privilege */
|
||||
PreviousState->Privileges[ChangeCount] = Token->Privileges[i];
|
||||
}
|
||||
|
||||
/* Does the caller want to apply the changes? */
|
||||
if (ApplyChanges)
|
||||
{
|
||||
/* Shall we remove the privilege? */
|
||||
if (NewAttributes & SE_PRIVILEGE_REMOVED)
|
||||
{
|
||||
/* Set the token as disabled and update flags for it */
|
||||
Token->Privileges[i].Attributes &= ~SE_PRIVILEGE_ENABLED;
|
||||
SepUpdateSinglePrivilegeFlagToken(Token, i);
|
||||
|
||||
/* Remove the privilege */
|
||||
SepRemovePrivilegeToken(Token, i);
|
||||
|
||||
*ChangesMade = TRUE;
|
||||
|
||||
/* Fix the running index and continue with next one */
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Set the new attributes and update flags */
|
||||
Token->Privileges[i].Attributes = NewAttributes;
|
||||
SepUpdateSinglePrivilegeFlagToken(Token, i);
|
||||
*ChangesMade = TRUE;
|
||||
}
|
||||
|
||||
/* Increment the change count */
|
||||
ChangeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the number of saved privileges */
|
||||
if (PreviousState != NULL)
|
||||
PreviousState->PrivilegeCount = ChangeCount;
|
||||
|
||||
/* Return the number of changed privileges */
|
||||
*ChangedPrivileges = ChangeCount;
|
||||
|
||||
/* Check if we missed some */
|
||||
if (!DisableAllPrivileges && (PrivilegeCount < NewStateCount))
|
||||
{
|
||||
return STATUS_NOT_ALL_ASSIGNED;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Private routine that iterates over the groups of an
|
||||
* access token to be adjusted as per on request by the
|
||||
* caller, where a group can be enabled or disabled.
|
||||
*
|
||||
* @param[in] Token
|
||||
* Access token where its groups are to be enabled or disabled.
|
||||
*
|
||||
* @param[in] NewState
|
||||
* A list of groups with new state attributes to be assigned to
|
||||
* the token.
|
||||
*
|
||||
* @param[in] NewStateCount
|
||||
* The captured count number of groups in the list.
|
||||
*
|
||||
* @param[in] ApplyChanges
|
||||
* If set to FALSE, the function will only iterate over the token's
|
||||
* groups without performing any kind of modification. If set to TRUE,
|
||||
* the changes will be applied immediately when the function has done
|
||||
* looping the groups.
|
||||
*
|
||||
* @param[in] ResetToDefaultStates
|
||||
* The function will reset the groups in an access token to default
|
||||
* states if set to TRUE. In such scenario the function ignores
|
||||
* NewState outright. Otherwise if set to FALSE, the function will
|
||||
* use NewState to assign the newly attributes to adjust the token's
|
||||
* groups. SE_GROUP_ENABLED_BY_DEFAULT is a flag indicator that is used
|
||||
* for such purpose.
|
||||
*
|
||||
* @param[out] ChangesMade
|
||||
* Returns TRUE if changes to token's groups have been made, otherwise
|
||||
* FALSE is returned. Bear in mind such changes aren't always deterministic.
|
||||
* See remarks for further details.
|
||||
*
|
||||
* @param[out] PreviousGroupsState
|
||||
* If requested by the caller, the function will return the previous state
|
||||
* of groups in an access token prior taking action on adjusting the token.
|
||||
* This is a UM (user mode) pointer and it's prone to raise exceptions
|
||||
* if such pointer address is not valid.
|
||||
*
|
||||
* @param[out] ChangedGroups
|
||||
* Returns the total number of changed groups in an access token. This
|
||||
* argument could also indicate the number of groups to be changed if
|
||||
* the calling thread hasn't chosen to apply the changes yet. A number
|
||||
* of 0 indicates no groups have been or to be changed because the groups'
|
||||
* attributes in a token are the same as the ones from NewState given by
|
||||
* the caller.
|
||||
*
|
||||
* @return
|
||||
* STATUS_SUCCESS is returned if the function has successfully completed
|
||||
* the operation of adjusting groups in a token. STATUS_CANT_DISABLE_MANDATORY
|
||||
* is returned if there was an attempt to disable a mandatory group which is
|
||||
* not possible. STATUS_CANT_ENABLE_DENY_ONLY is returned if there was an attempt
|
||||
* to enable a "use for Deny only" group which is not allowed, that is, a restricted
|
||||
* group. STATUS_NOT_ALL_ASSIGNED is returned if not all the groups are actually
|
||||
* assigned to the token.
|
||||
*
|
||||
* @remarks
|
||||
* Token groups adjusting can be judged to be deterministic or not based on the
|
||||
* NT status code value. That is, STATUS_SUCCESS indicates the function not only
|
||||
* has iterated over the whole groups in a token, it also has applied the changes
|
||||
* thoroughly without impediment and the results perfectly match with the request
|
||||
* desired by the caller. In this situation the condition is deemed deterministic.
|
||||
* In a different situation however, if the status code was STATUS_NOT_ALL_ASSIGNED,
|
||||
* the function would still continue looping the groups in a token and apply the
|
||||
* changes whenever possible where the respective groups actually exist in the
|
||||
* token. This kind of situation is deemed as indeterministic.
|
||||
* For STATUS_CANT_DISABLE_MANDATORY and STATUS_CANT_ENABLE_DENY_ONLY the scenario
|
||||
* is even more indeterministic as the iteration of groups comes to a halt thus
|
||||
* leaving all other possible groups to be adjusted.
|
||||
*/
|
||||
static
|
||||
NTSTATUS
|
||||
SepAdjustGroups(
|
||||
_In_ PTOKEN Token,
|
||||
_In_opt_ PSID_AND_ATTRIBUTES NewState,
|
||||
_In_ ULONG NewStateCount,
|
||||
_In_ BOOLEAN ApplyChanges,
|
||||
_In_ BOOLEAN ResetToDefaultStates,
|
||||
_Out_ PBOOLEAN ChangesMade,
|
||||
_Out_opt_ PTOKEN_GROUPS PreviousGroupsState,
|
||||
_Out_ PULONG ChangedGroups)
|
||||
{
|
||||
ULONG GroupsInToken, GroupsInList;
|
||||
ULONG ChangeCount, GroupsCount, NewAttributes;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
/* Ensure that the token we get is valid */
|
||||
ASSERT(Token);
|
||||
|
||||
/* Initialize the counters and begin the work */
|
||||
*ChangesMade = FALSE;
|
||||
GroupsCount = 0;
|
||||
ChangeCount = 0;
|
||||
|
||||
/* Begin looping all the groups in the token */
|
||||
for (GroupsInToken = 0; GroupsInToken < Token->UserAndGroupCount; GroupsInToken++)
|
||||
{
|
||||
/* Does the caller want to reset groups to default states? */
|
||||
if (ResetToDefaultStates)
|
||||
{
|
||||
/*
|
||||
* SE_GROUP_ENABLED_BY_DEFAULT is a special indicator that informs us
|
||||
* if a certain group has been enabled by default or not. In case
|
||||
* a group is enabled by default but it is not currently enabled then
|
||||
* at that point we must enable it back by default. For now just
|
||||
* assign the respective SE_GROUP_ENABLED attribute as we'll do the
|
||||
* eventual work later.
|
||||
*/
|
||||
if ((Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_ENABLED_BY_DEFAULT) &&
|
||||
(Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_ENABLED) == 0)
|
||||
{
|
||||
NewAttributes = Token->UserAndGroups[GroupsInToken].Attributes |= SE_GROUP_ENABLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlike the case above, a group that hasn't been enabled by
|
||||
* default but it's currently enabled then we must disable
|
||||
* it back.
|
||||
*/
|
||||
if ((Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_ENABLED_BY_DEFAULT) == 0 &&
|
||||
(Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_ENABLED))
|
||||
{
|
||||
NewAttributes = Token->UserAndGroups[GroupsInToken].Attributes & ~SE_GROUP_ENABLED;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Loop the provided groups in the list then */
|
||||
for (GroupsInList = 0; GroupsInList < NewStateCount; GroupsInList++)
|
||||
{
|
||||
/* Does this group exist in the token? */
|
||||
if (RtlEqualSid(&Token->UserAndGroups[GroupsInToken].Sid,
|
||||
&NewState[GroupsInList].Sid))
|
||||
{
|
||||
/*
|
||||
* This is the group that we're looking for.
|
||||
* However, it could be that the group is a
|
||||
* mandatory group which we are not allowed
|
||||
* and cannot disable it.
|
||||
*/
|
||||
if ((Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_MANDATORY) &&
|
||||
(NewState[GroupsInList].Attributes & SE_GROUP_ENABLED) == 0)
|
||||
{
|
||||
/* It is mandatory, forget about this group */
|
||||
DPRINT1("SepAdjustGroups(): The SID group is mandatory!\n");
|
||||
return STATUS_CANT_DISABLE_MANDATORY;
|
||||
}
|
||||
|
||||
/*
|
||||
* We've to ensure that apart the group mustn't be
|
||||
* mandatory, it mustn't be a restricted group as
|
||||
* well. That is, the group is marked with
|
||||
* SE_GROUP_USE_FOR_DENY_ONLY flag and no one
|
||||
* can enable it because it's for "deny" use only.
|
||||
*/
|
||||
if ((Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_USE_FOR_DENY_ONLY) &&
|
||||
(NewState[GroupsInList].Attributes & SE_GROUP_ENABLED))
|
||||
{
|
||||
/* This group is restricted, forget about it */
|
||||
DPRINT1("SepAdjustGroups(): The SID group is for use deny only!\n");
|
||||
return STATUS_CANT_ENABLE_DENY_ONLY;
|
||||
}
|
||||
|
||||
/* Copy the attributes and stop searching */
|
||||
NewAttributes = NewState[GroupsInList].Attributes;
|
||||
NewAttributes &= SE_GROUP_ENABLED;
|
||||
NewAttributes = Token->UserAndGroups[GroupsInToken].Attributes & ~SE_GROUP_ENABLED;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Did we find the specific group we wanted? */
|
||||
if (GroupsInList == NewStateCount)
|
||||
{
|
||||
/* We didn't, continue with the next token's group */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Count the group that we found it */
|
||||
GroupsCount++;
|
||||
|
||||
/* Does the token have the same attributes as the caller requested them? */
|
||||
if (Token->UserAndGroups[GroupsInToken].Attributes != NewAttributes)
|
||||
{
|
||||
/*
|
||||
* No, then it's time to make some adjustment to the
|
||||
* token's groups. Does the caller want the previous states
|
||||
* of groups?
|
||||
*/
|
||||
if (PreviousGroupsState != NULL)
|
||||
{
|
||||
PreviousGroupsState->Groups[ChangeCount] = Token->UserAndGroups[GroupsInToken];
|
||||
}
|
||||
|
||||
/* Time to apply the changes now? */
|
||||
if (ApplyChanges)
|
||||
{
|
||||
/* The caller gave us consent, apply and report that we made changes! */
|
||||
Token->UserAndGroups[GroupsInToken].Attributes = NewAttributes;
|
||||
*ChangesMade = TRUE;
|
||||
}
|
||||
|
||||
/* Increment the count change */
|
||||
ChangeCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Report the number of previous saved groups */
|
||||
if (PreviousGroupsState != NULL)
|
||||
{
|
||||
PreviousGroupsState->GroupCount = ChangeCount;
|
||||
}
|
||||
|
||||
/* Report the number of changed groups */
|
||||
*ChangedGroups = ChangeCount;
|
||||
|
||||
/* Did we miss some groups? */
|
||||
if (!ResetToDefaultStates && (GroupsCount < NewStateCount))
|
||||
{
|
||||
/*
|
||||
* If we're at this stage then we are in a situation
|
||||
* where the adjust changes done to token's groups is
|
||||
* not deterministic as the caller might have wanted
|
||||
* as per NewState parameter.
|
||||
*/
|
||||
DPRINT1("SepAdjustGroups(): The token hasn't all the groups assigned!\n");
|
||||
return STATUS_NOT_ALL_ASSIGNED;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* SYSTEM CALLS ***************************************************************/
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Removes a certain amount of privileges of a token based upon the request
|
||||
* by the caller.
|
||||
*
|
||||
* @param[in,out] Token
|
||||
* Token handle where the privileges are about to be modified.
|
||||
*
|
||||
* @param[in] DisableAllPrivileges
|
||||
* If set to TRUE, the function disables all the privileges.
|
||||
*
|
||||
* @param[in] NewState
|
||||
* A new list of privileges that the function will use it accordingly to
|
||||
* either disable or enable the said privileges and change them.
|
||||
*
|
||||
* @param[in] NewStateCount
|
||||
* The new total number count of privileges.
|
||||
*
|
||||
* @param[out] PreviousState
|
||||
* If specified, the function will return the previous state list of privileges.
|
||||
*
|
||||
* @param[in] ApplyChanges
|
||||
* If set to TRUE, the function will immediatelly apply the changes onto the
|
||||
* token's privileges.
|
||||
*
|
||||
* @param[out] ChangedPrivileges
|
||||
* The returned count number of changed privileges.
|
||||
*
|
||||
* @param[out] ChangesMade
|
||||
* If TRUE, the function has made changes to the token's privileges. FALSE
|
||||
* otherwise.
|
||||
*
|
||||
* @return
|
||||
* Returns STATUS_SUCCESS if the function has successfully changed the list
|
||||
* of privileges. STATUS_NOT_ALL_ASSIGNED is returned if not every privilege
|
||||
* has been changed.
|
||||
*/
|
||||
_Must_inspect_result_
|
||||
__kernel_entry
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
NtAdjustPrivilegesToken(
|
||||
_In_ HANDLE TokenHandle,
|
||||
_In_ BOOLEAN DisableAllPrivileges,
|
||||
_In_opt_ PTOKEN_PRIVILEGES NewState,
|
||||
_In_ ULONG BufferLength,
|
||||
_Out_writes_bytes_to_opt_(BufferLength,*ReturnLength)
|
||||
PTOKEN_PRIVILEGES PreviousState,
|
||||
_When_(PreviousState!=NULL, _Out_) PULONG ReturnLength)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
KPROCESSOR_MODE PreviousMode;
|
||||
PTOKEN Token;
|
||||
PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
|
||||
ULONG CapturedCount = 0;
|
||||
ULONG CapturedLength = 0;
|
||||
ULONG NewStateSize = 0;
|
||||
ULONG ChangeCount;
|
||||
ULONG RequiredLength;
|
||||
BOOLEAN ChangesMade = FALSE;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
DPRINT("NtAdjustPrivilegesToken() called\n");
|
||||
|
||||
/* Fail, if we do not disable all privileges but NewState is NULL */
|
||||
if (DisableAllPrivileges == FALSE && NewState == NULL)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
PreviousMode = KeGetPreviousMode();
|
||||
if (PreviousMode != KernelMode)
|
||||
{
|
||||
_SEH2_TRY
|
||||
{
|
||||
/* Probe NewState */
|
||||
if (DisableAllPrivileges == FALSE)
|
||||
{
|
||||
/* First probe the header */
|
||||
ProbeForRead(NewState, sizeof(TOKEN_PRIVILEGES), sizeof(ULONG));
|
||||
|
||||
CapturedCount = NewState->PrivilegeCount;
|
||||
NewStateSize = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[CapturedCount]);
|
||||
|
||||
ProbeForRead(NewState, NewStateSize, sizeof(ULONG));
|
||||
}
|
||||
|
||||
/* Probe PreviousState and ReturnLength */
|
||||
if (PreviousState != NULL)
|
||||
{
|
||||
ProbeForWrite(PreviousState, BufferLength, sizeof(ULONG));
|
||||
ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG));
|
||||
}
|
||||
}
|
||||
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
/* Return the exception code */
|
||||
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
||||
}
|
||||
_SEH2_END;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This is kernel mode, we trust the caller */
|
||||
if (DisableAllPrivileges == FALSE)
|
||||
CapturedCount = NewState->PrivilegeCount;
|
||||
}
|
||||
|
||||
/* Do we need to capture the new state? */
|
||||
if (DisableAllPrivileges == FALSE)
|
||||
{
|
||||
_SEH2_TRY
|
||||
{
|
||||
/* Capture the new state array of privileges */
|
||||
Status = SeCaptureLuidAndAttributesArray(NewState->Privileges,
|
||||
CapturedCount,
|
||||
PreviousMode,
|
||||
NULL,
|
||||
0,
|
||||
PagedPool,
|
||||
TRUE,
|
||||
&CapturedPrivileges,
|
||||
&CapturedLength);
|
||||
}
|
||||
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
/* Return the exception code */
|
||||
Status = _SEH2_GetExceptionCode();
|
||||
}
|
||||
_SEH2_END;
|
||||
|
||||
if (!NT_SUCCESS(Status))
|
||||
return Status;
|
||||
}
|
||||
|
||||
/* Reference the token */
|
||||
Status = ObReferenceObjectByHandle(TokenHandle,
|
||||
TOKEN_ADJUST_PRIVILEGES | (PreviousState != NULL ? TOKEN_QUERY : 0),
|
||||
SeTokenObjectType,
|
||||
PreviousMode,
|
||||
(PVOID*)&Token,
|
||||
NULL);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DPRINT1("Failed to reference token (Status 0x%lx)\n", Status);
|
||||
|
||||
/* Release the captured privileges */
|
||||
if (CapturedPrivileges != NULL)
|
||||
{
|
||||
SeReleaseLuidAndAttributesArray(CapturedPrivileges,
|
||||
PreviousMode,
|
||||
TRUE);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
/* Lock the token */
|
||||
SepAcquireTokenLockExclusive(Token);
|
||||
|
||||
/* Count the privileges that need to be changed, do not apply them yet */
|
||||
Status = SepAdjustPrivileges(Token,
|
||||
DisableAllPrivileges,
|
||||
CapturedPrivileges,
|
||||
CapturedCount,
|
||||
NULL,
|
||||
FALSE,
|
||||
&ChangeCount,
|
||||
&ChangesMade);
|
||||
|
||||
/* Check if the caller asked for the previous state */
|
||||
if (PreviousState != NULL)
|
||||
{
|
||||
/* Calculate the required length */
|
||||
RequiredLength = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[ChangeCount]);
|
||||
|
||||
/* Try to return the required buffer length */
|
||||
_SEH2_TRY
|
||||
{
|
||||
*ReturnLength = RequiredLength;
|
||||
}
|
||||
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
/* Do cleanup and return the exception code */
|
||||
Status = _SEH2_GetExceptionCode();
|
||||
_SEH2_YIELD(goto Cleanup);
|
||||
}
|
||||
_SEH2_END;
|
||||
|
||||
/* Fail, if the buffer length is smaller than the required length */
|
||||
if (BufferLength < RequiredLength)
|
||||
{
|
||||
Status = STATUS_BUFFER_TOO_SMALL;
|
||||
goto Cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now enter SEH, since we might return the old privileges */
|
||||
_SEH2_TRY
|
||||
{
|
||||
/* This time apply the changes */
|
||||
Status = SepAdjustPrivileges(Token,
|
||||
DisableAllPrivileges,
|
||||
CapturedPrivileges,
|
||||
CapturedCount,
|
||||
PreviousState,
|
||||
TRUE,
|
||||
&ChangeCount,
|
||||
&ChangesMade);
|
||||
}
|
||||
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
/* Do cleanup and return the exception code */
|
||||
Status = _SEH2_GetExceptionCode();
|
||||
ChangesMade = TRUE; // Force write.
|
||||
_SEH2_YIELD(goto Cleanup);
|
||||
}
|
||||
_SEH2_END;
|
||||
|
||||
Cleanup:
|
||||
/* Touch the token if we made changes */
|
||||
if (ChangesMade)
|
||||
ExAllocateLocallyUniqueId(&Token->ModifiedId);
|
||||
|
||||
/* Unlock and dereference the token */
|
||||
SepReleaseTokenLock(Token);
|
||||
ObDereferenceObject(Token);
|
||||
|
||||
/* Release the captured privileges */
|
||||
if (CapturedPrivileges != NULL)
|
||||
{
|
||||
SeReleaseLuidAndAttributesArray(CapturedPrivileges,
|
||||
PreviousMode,
|
||||
TRUE);
|
||||
}
|
||||
|
||||
DPRINT("NtAdjustPrivilegesToken() done\n");
|
||||
return Status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Changes the list of groups by enabling or disabling them
|
||||
* in an access token. Unlike NtAdjustPrivilegesToken,
|
||||
* this API routine does not remove groups.
|
||||
*
|
||||
* @param[in] TokenHandle
|
||||
* Token handle where the list of groups SID are to be adjusted.
|
||||
* The access token must have TOKEN_ADJUST_GROUPS access right
|
||||
* in order to change the groups in a token. The token must also
|
||||
* have TOKEN_QUERY access right if the caller requests the previous
|
||||
* states of groups list, that is, PreviousState is not NULL.
|
||||
*
|
||||
* @param[in] ResetToDefault
|
||||
* If set to TRUE, the function resets the list of groups to default
|
||||
* enabled and disabled states. NewState is ignored in this case.
|
||||
* Otherwise if the parameter is set to FALSE, the function expects
|
||||
* a new list of groups from NewState to be adjusted within the token.
|
||||
*
|
||||
* @param[in] NewState
|
||||
* A new list of groups SID that the function will use it accordingly to
|
||||
* modify the current list of groups SID of a token.
|
||||
*
|
||||
* @param[in] BufferLength
|
||||
* The length size of the buffer that is pointed by the NewState parameter
|
||||
* argument, in bytes.
|
||||
*
|
||||
* @param[out] PreviousState
|
||||
* If specified, the function will return to the caller the old list of groups
|
||||
* SID. If this parameter is NULL, ReturnLength must also be NULL.
|
||||
*
|
||||
* @param[out] ReturnLength
|
||||
* If specified, the function will return the total size length of the old list
|
||||
* of groups SIDs, in bytes.
|
||||
*
|
||||
* @return
|
||||
* STATUS_SUCCESS is returned if the function has successfully adjusted the
|
||||
* token's groups. STATUS_INVALID_PARAMETER is returned if the caller has
|
||||
* submitted one or more invalid parameters, that is, the caller didn't want
|
||||
* to reset the groups to default state but no NewState argument list has been
|
||||
* provided. STATUS_BUFFER_TOO_SMALL is returned if the buffer length given
|
||||
* by the caller is smaller than the required length size. A failure NTSTATUS
|
||||
* code is returned otherwise.
|
||||
*/
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
NtAdjustGroupsToken(
|
||||
_In_ HANDLE TokenHandle,
|
||||
_In_ BOOLEAN ResetToDefault,
|
||||
_In_ PTOKEN_GROUPS NewState,
|
||||
_In_ ULONG BufferLength,
|
||||
_Out_writes_bytes_to_opt_(BufferLength, *ReturnLength)
|
||||
PTOKEN_GROUPS PreviousState,
|
||||
_When_(PreviousState != NULL, _Out_) PULONG ReturnLength)
|
||||
{
|
||||
PTOKEN Token;
|
||||
NTSTATUS Status;
|
||||
KPROCESSOR_MODE PreviousMode;
|
||||
ULONG ChangeCount, RequiredLength;
|
||||
ULONG CapturedCount = 0;
|
||||
ULONG CapturedLength = 0;
|
||||
ULONG NewStateSize = 0;
|
||||
PSID_AND_ATTRIBUTES CapturedGroups = NULL;
|
||||
BOOLEAN ChangesMade = FALSE;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
/*
|
||||
* If the caller doesn't want to reset the groups of an
|
||||
* access token to default states then at least we must
|
||||
* expect a list of groups to be adjusted based on NewState
|
||||
* parameter. Otherwise bail out because the caller has
|
||||
* no idea what they're doing.
|
||||
*/
|
||||
if (!ResetToDefault && !NewState)
|
||||
{
|
||||
DPRINT1("NtAdjustGroupsToken(): The caller hasn't provided any list of groups to adjust!\n");
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
PreviousMode = ExGetPreviousMode();
|
||||
|
||||
if (PreviousMode != KernelMode)
|
||||
{
|
||||
_SEH2_TRY
|
||||
{
|
||||
/* Probe NewState */
|
||||
if (!ResetToDefault)
|
||||
{
|
||||
/* Probe the header */
|
||||
ProbeForRead(NewState, sizeof(*NewState), sizeof(ULONG));
|
||||
|
||||
CapturedCount = NewState->GroupCount;
|
||||
NewStateSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedCount]);
|
||||
|
||||
ProbeForRead(NewState, NewStateSize, sizeof(ULONG));
|
||||
}
|
||||
|
||||
if (PreviousState != NULL)
|
||||
{
|
||||
ProbeForWrite(PreviousState, BufferLength, sizeof(ULONG));
|
||||
ProbeForWrite(ReturnLength, sizeof(*ReturnLength), sizeof(ULONG));
|
||||
}
|
||||
}
|
||||
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
/* Return the exception code */
|
||||
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
||||
}
|
||||
_SEH2_END;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* We're calling directly from the kernel, just retrieve
|
||||
* the number count of captured groups outright.
|
||||
*/
|
||||
if (!ResetToDefault)
|
||||
{
|
||||
CapturedCount = NewState->GroupCount;
|
||||
}
|
||||
}
|
||||
|
||||
/* Time to capture the NewState list */
|
||||
if (!ResetToDefault)
|
||||
{
|
||||
_SEH2_TRY
|
||||
{
|
||||
Status = SeCaptureSidAndAttributesArray(NewState->Groups,
|
||||
CapturedCount,
|
||||
PreviousMode,
|
||||
NULL,
|
||||
0,
|
||||
PagedPool,
|
||||
TRUE,
|
||||
&CapturedGroups,
|
||||
&CapturedLength);
|
||||
}
|
||||
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
Status = _SEH2_GetExceptionCode();
|
||||
}
|
||||
_SEH2_END;
|
||||
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DPRINT1("NtAdjustGroupsToken(): Failed to capture the NewState list of groups (Status 0x%lx)\n", Status);
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
/* Time to reference the token */
|
||||
Status = ObReferenceObjectByHandle(TokenHandle,
|
||||
TOKEN_ADJUST_GROUPS | (PreviousState != NULL ? TOKEN_QUERY : 0),
|
||||
SeTokenObjectType,
|
||||
PreviousMode,
|
||||
(PVOID*)&Token,
|
||||
NULL);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
/* We couldn't reference the access token, bail out */
|
||||
DPRINT1("NtAdjustGroupsToken(): Failed to reference the token (Status 0x%lx)\n", Status);
|
||||
|
||||
if (CapturedGroups != NULL)
|
||||
{
|
||||
SeReleaseSidAndAttributesArray(CapturedGroups,
|
||||
PreviousMode,
|
||||
TRUE);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
/* Lock the token */
|
||||
SepAcquireTokenLockExclusive(Token);
|
||||
|
||||
/* Count the number of groups to be changed */
|
||||
Status = SepAdjustGroups(Token,
|
||||
CapturedGroups,
|
||||
CapturedCount,
|
||||
FALSE,
|
||||
ResetToDefault,
|
||||
&ChangesMade,
|
||||
NULL,
|
||||
&ChangeCount);
|
||||
|
||||
/* Does the caller want the previous state of groups? */
|
||||
if (PreviousState != NULL)
|
||||
{
|
||||
/* Calculate the required length */
|
||||
RequiredLength = FIELD_OFFSET(TOKEN_GROUPS, Groups[ChangeCount]);
|
||||
|
||||
/* Return the required length to the caller */
|
||||
_SEH2_TRY
|
||||
{
|
||||
*ReturnLength = RequiredLength;
|
||||
}
|
||||
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
/* Bail out and return the exception code */
|
||||
Status = _SEH2_GetExceptionCode();
|
||||
_SEH2_YIELD(goto Quit);
|
||||
}
|
||||
_SEH2_END;
|
||||
|
||||
/* The buffer length provided is smaller than the required length, bail out */
|
||||
if (BufferLength < RequiredLength)
|
||||
{
|
||||
Status = STATUS_BUFFER_TOO_SMALL;
|
||||
goto Quit;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now it's time to apply changes. Wrap the code
|
||||
* in SEH as we are returning the old groups state
|
||||
* list to the caller since PreviousState is a
|
||||
* UM pointer.
|
||||
*/
|
||||
_SEH2_TRY
|
||||
{
|
||||
Status = SepAdjustGroups(Token,
|
||||
CapturedGroups,
|
||||
CapturedCount,
|
||||
TRUE,
|
||||
ResetToDefault,
|
||||
&ChangesMade,
|
||||
PreviousState,
|
||||
&ChangeCount);
|
||||
}
|
||||
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
/* Bail out and return the exception code */
|
||||
Status = _SEH2_GetExceptionCode();
|
||||
|
||||
/* Force the write as we touched the token still */
|
||||
ChangesMade = TRUE;
|
||||
_SEH2_YIELD(goto Quit);
|
||||
}
|
||||
_SEH2_END;
|
||||
|
||||
Quit:
|
||||
/* Allocate a new ID for the token as we made changes */
|
||||
if (ChangesMade)
|
||||
ExAllocateLocallyUniqueId(&Token->ModifiedId);
|
||||
|
||||
/* Unlock and dereference the token */
|
||||
SepReleaseTokenLock(Token);
|
||||
ObDereferenceObject(Token);
|
||||
|
||||
/* Release the captured groups */
|
||||
if (CapturedGroups != NULL)
|
||||
{
|
||||
SeReleaseSidAndAttributesArray(CapturedGroups,
|
||||
PreviousMode,
|
||||
TRUE);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
/* EOF */
|
1588
ntoskrnl/se/tokencls.c
Normal file
1588
ntoskrnl/se/tokencls.c
Normal file
File diff suppressed because it is too large
Load diff
2201
ntoskrnl/se/tokenlif.c
Normal file
2201
ntoskrnl/se/tokenlif.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue