reactos/ntoskrnl/se/access.c
Hermès Bélusca-Maïto 986bf4c407
[NTOS:SE] In SepCreateClientSecurity(), fix the impersonation level value passed to the SeCopyClientToken() call.
Caught while debugging, in the case the ImpersonationLevel value was
uninitialized, due to the fact it was left untouched on purpose by
PsReferenceEffectiveToken().
2018-06-27 23:40:14 +02:00

633 lines
18 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: ntoskrnl/se/access.c
* PURPOSE: Access state functions
*
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net) -
* Based on patch by Javier M. Mellid
*/
/* INCLUDES *******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS ********************************************************************/
ERESOURCE SepSubjectContextLock;
/* PRIVATE FUNCTIONS **********************************************************/
BOOLEAN
NTAPI
SepSidInTokenEx(IN PACCESS_TOKEN _Token,
IN PSID PrincipalSelfSid,
IN PSID _Sid,
IN BOOLEAN Deny,
IN BOOLEAN Restricted)
{
ULONG i;
PTOKEN Token = (PTOKEN)_Token;
PISID TokenSid, Sid = (PISID)_Sid;
PSID_AND_ATTRIBUTES SidAndAttributes;
ULONG SidCount, SidLength;
USHORT SidMetadata;
PAGED_CODE();
/* Not yet supported */
ASSERT(PrincipalSelfSid == NULL);
ASSERT(Restricted == FALSE);
/* 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 (i = 0; i < SidCount; i++)
{
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 */
if ((!(i) && !(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;
}
BOOLEAN
NTAPI
SepSidInToken(IN PACCESS_TOKEN _Token,
IN PSID Sid)
{
/* Call extended API */
return SepSidInTokenEx(_Token, NULL, Sid, FALSE, FALSE);
}
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;
}
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);
}
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 ***********************************************************/
/*
* @implemented
*/
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);
}
/*
* @implemented
*/
VOID
NTAPI
SeCaptureSubjectContext(OUT PSECURITY_SUBJECT_CONTEXT SubjectContext)
{
/* Call the extended API */
SeCaptureSubjectContextEx(PsGetCurrentThread(),
PsGetCurrentProcess(),
SubjectContext);
}
/*
* @implemented
*/
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);
}
/*
* @implemented
*/
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);
}
/*
* @implemented
*/
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;
}
/*
* @implemented
*/
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_MASK AccessMask = Access;
PTOKEN Token;
PAGED_CODE();
/* Map the Generic Acess to Specific Access if we have a Mapping */
if ((Access & GENERIC_ACCESS) && (GenericMapping))
{
RtlMapGenericMask(&AccessMask, GenericMapping);
}
/* Initialize the Access State */
RtlZeroMemory(AccessState, sizeof(ACCESS_STATE));
ASSERT(AccessState->SecurityDescriptor == NULL);
ASSERT(AccessState->PrivilegesAllocated == FALSE);
/* Initialize and save aux data */
RtlZeroMemory(AuxData, sizeof(AUX_ACCESS_DATA));
AccessState->AuxData = AuxData;
/* Capture the Subject Context */
SeCaptureSubjectContextEx(Thread,
Process,
&AccessState->SubjectSecurityContext);
/* Set Access State Data */
AccessState->RemainingDesiredAccess = AccessMask;
AccessState->OriginalDesiredAccess = AccessMask;
ExAllocateLocallyUniqueId(&AccessState->OperationID);
/* Get the Token to use */
Token = SeQuerySubjectContextToken(&AccessState->SubjectSecurityContext);
/* Check for Travers Privilege */
if (Token->TokenFlags & TOKEN_HAS_TRAVERSE_PRIVILEGE)
{
/* Preserve the Traverse Privilege */
AccessState->Flags = TOKEN_HAS_TRAVERSE_PRIVILEGE;
}
/* Set the Auxiliary Data */
AuxData->PrivilegeSet = (PPRIVILEGE_SET)((ULONG_PTR)AccessState +
FIELD_OFFSET(ACCESS_STATE,
Privileges));
if (GenericMapping) AuxData->GenericMapping = *GenericMapping;
/* Return Sucess */
return STATUS_SUCCESS;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
SeCreateAccessState(IN OUT PACCESS_STATE AccessState,
IN PAUX_ACCESS_DATA AuxData,
IN ACCESS_MASK Access,
IN PGENERIC_MAPPING GenericMapping)
{
PAGED_CODE();
/* Call the extended API */
return SeCreateAccessStateEx(PsGetCurrentThread(),
PsGetCurrentProcess(),
AccessState,
AuxData,
Access,
GenericMapping);
}
/*
* @implemented
*/
VOID
NTAPI
SeDeleteAccessState(IN PACCESS_STATE AccessState)
{
PAUX_ACCESS_DATA AuxData;
PAGED_CODE();
/* Get the Auxiliary Data */
AuxData = AccessState->AuxData;
/* Deallocate Privileges */
if (AccessState->PrivilegesAllocated)
ExFreePoolWithTag(AuxData->PrivilegeSet, TAG_PRIVILEGE_SET);
/* Deallocate Name and Type Name */
if (AccessState->ObjectName.Buffer)
{
ExFreePool(AccessState->ObjectName.Buffer);
}
if (AccessState->ObjectTypeName.Buffer)
{
ExFreePool(AccessState->ObjectTypeName.Buffer);
}
/* Release the Subject Context */
SeReleaseSubjectContext(&AccessState->SubjectSecurityContext);
}
/*
* @implemented
*/
VOID
NTAPI
SeSetAccessStateGenericMapping(IN PACCESS_STATE AccessState,
IN PGENERIC_MAPPING GenericMapping)
{
PAGED_CODE();
/* Set the Generic Mapping */
((PAUX_ACCESS_DATA)AccessState->AuxData)->GenericMapping = *GenericMapping;
}
/*
* @implemented
*/
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;
}
/*
* @implemented
*/
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;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
SeImpersonateClientEx(IN PSECURITY_CLIENT_CONTEXT ClientContext,
IN PETHREAD ServerThread OPTIONAL)
{
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);
}
/*
* @implemented
*/
VOID
NTAPI
SeImpersonateClient(IN PSECURITY_CLIENT_CONTEXT ClientContext,
IN PETHREAD ServerThread OPTIONAL)
{
PAGED_CODE();
/* Call the new API */
SeImpersonateClientEx(ClientContext, ServerThread);
}
/* EOF */