reactos/ntoskrnl/se/sid.c
George Bișoc 9a2c62b544
[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.
2022-05-29 20:22:19 +02:00

994 lines
35 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Security Identifier (SID) implementation support and handling
* COPYRIGHT: Copyright David Welch <welch@cwcom.net>
*/
/* INCLUDES *******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS ********************************************************************/
#define SE_MAXIMUM_GROUP_LIMIT 0x1000
SID_IDENTIFIER_AUTHORITY SeNullSidAuthority = {SECURITY_NULL_SID_AUTHORITY};
SID_IDENTIFIER_AUTHORITY SeWorldSidAuthority = {SECURITY_WORLD_SID_AUTHORITY};
SID_IDENTIFIER_AUTHORITY SeLocalSidAuthority = {SECURITY_LOCAL_SID_AUTHORITY};
SID_IDENTIFIER_AUTHORITY SeCreatorSidAuthority = {SECURITY_CREATOR_SID_AUTHORITY};
SID_IDENTIFIER_AUTHORITY SeNtSidAuthority = {SECURITY_NT_AUTHORITY};
PSID SeNullSid = NULL;
PSID SeWorldSid = NULL;
PSID SeLocalSid = NULL;
PSID SeCreatorOwnerSid = NULL;
PSID SeCreatorGroupSid = NULL;
PSID SeCreatorOwnerServerSid = NULL;
PSID SeCreatorGroupServerSid = NULL;
PSID SeNtAuthoritySid = NULL;
PSID SeDialupSid = NULL;
PSID SeNetworkSid = NULL;
PSID SeBatchSid = NULL;
PSID SeInteractiveSid = NULL;
PSID SeServiceSid = NULL;
PSID SePrincipalSelfSid = NULL;
PSID SeLocalSystemSid = NULL;
PSID SeAuthenticatedUserSid = NULL;
PSID SeRestrictedCodeSid = NULL;
PSID SeAliasAdminsSid = NULL;
PSID SeAliasUsersSid = NULL;
PSID SeAliasGuestsSid = NULL;
PSID SeAliasPowerUsersSid = NULL;
PSID SeAliasAccountOpsSid = NULL;
PSID SeAliasSystemOpsSid = NULL;
PSID SeAliasPrintOpsSid = NULL;
PSID SeAliasBackupOpsSid = NULL;
PSID SeAuthenticatedUsersSid = NULL;
PSID SeRestrictedSid = NULL;
PSID SeAnonymousLogonSid = NULL;
PSID SeLocalServiceSid = NULL;
PSID SeNetworkServiceSid = NULL;
typedef struct _SID_VALIDATE
{
UCHAR SubAuthorityCount;
PISID ProbeSid;
} SID_VALIDATE, *PSID_VALIDATE;
/* FUNCTIONS ******************************************************************/
/**
* @brief
* Frees all the known initialized SIDs in the system from the memory.
*
* @return
* Nothing.
*/
VOID
NTAPI
FreeInitializedSids(VOID)
{
if (SeNullSid) ExFreePoolWithTag(SeNullSid, TAG_SID);
if (SeWorldSid) ExFreePoolWithTag(SeWorldSid, TAG_SID);
if (SeLocalSid) ExFreePoolWithTag(SeLocalSid, TAG_SID);
if (SeCreatorOwnerSid) ExFreePoolWithTag(SeCreatorOwnerSid, TAG_SID);
if (SeCreatorGroupSid) ExFreePoolWithTag(SeCreatorGroupSid, TAG_SID);
if (SeCreatorOwnerServerSid) ExFreePoolWithTag(SeCreatorOwnerServerSid, TAG_SID);
if (SeCreatorGroupServerSid) ExFreePoolWithTag(SeCreatorGroupServerSid, TAG_SID);
if (SeNtAuthoritySid) ExFreePoolWithTag(SeNtAuthoritySid, TAG_SID);
if (SeDialupSid) ExFreePoolWithTag(SeDialupSid, TAG_SID);
if (SeNetworkSid) ExFreePoolWithTag(SeNetworkSid, TAG_SID);
if (SeBatchSid) ExFreePoolWithTag(SeBatchSid, TAG_SID);
if (SeInteractiveSid) ExFreePoolWithTag(SeInteractiveSid, TAG_SID);
if (SeServiceSid) ExFreePoolWithTag(SeServiceSid, TAG_SID);
if (SePrincipalSelfSid) ExFreePoolWithTag(SePrincipalSelfSid, TAG_SID);
if (SeLocalSystemSid) ExFreePoolWithTag(SeLocalSystemSid, TAG_SID);
if (SeAuthenticatedUserSid) ExFreePoolWithTag(SeAuthenticatedUserSid, TAG_SID);
if (SeRestrictedCodeSid) ExFreePoolWithTag(SeRestrictedCodeSid, TAG_SID);
if (SeAliasAdminsSid) ExFreePoolWithTag(SeAliasAdminsSid, TAG_SID);
if (SeAliasUsersSid) ExFreePoolWithTag(SeAliasUsersSid, TAG_SID);
if (SeAliasGuestsSid) ExFreePoolWithTag(SeAliasGuestsSid, TAG_SID);
if (SeAliasPowerUsersSid) ExFreePoolWithTag(SeAliasPowerUsersSid, TAG_SID);
if (SeAliasAccountOpsSid) ExFreePoolWithTag(SeAliasAccountOpsSid, TAG_SID);
if (SeAliasSystemOpsSid) ExFreePoolWithTag(SeAliasSystemOpsSid, TAG_SID);
if (SeAliasPrintOpsSid) ExFreePoolWithTag(SeAliasPrintOpsSid, TAG_SID);
if (SeAliasBackupOpsSid) ExFreePoolWithTag(SeAliasBackupOpsSid, TAG_SID);
if (SeAuthenticatedUsersSid) ExFreePoolWithTag(SeAuthenticatedUsersSid, TAG_SID);
if (SeRestrictedSid) ExFreePoolWithTag(SeRestrictedSid, TAG_SID);
if (SeAnonymousLogonSid) ExFreePoolWithTag(SeAnonymousLogonSid, TAG_SID);
}
/**
* @brief
* Initializes all the SIDs known in the system.
*
* @return
* Returns TRUE if all the SIDs have been initialized,
* FALSE otherwise.
*/
CODE_SEG("INIT")
BOOLEAN
NTAPI
SepInitSecurityIDs(VOID)
{
ULONG SidLength0;
ULONG SidLength1;
ULONG SidLength2;
PULONG SubAuthority;
SidLength0 = RtlLengthRequiredSid(0);
SidLength1 = RtlLengthRequiredSid(1);
SidLength2 = RtlLengthRequiredSid(2);
/* create NullSid */
SeNullSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SeWorldSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SeLocalSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SeCreatorOwnerSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SeCreatorGroupSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SeCreatorOwnerServerSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SeCreatorGroupServerSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SeNtAuthoritySid = ExAllocatePoolWithTag(PagedPool, SidLength0, TAG_SID);
SeDialupSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SeNetworkSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SeBatchSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SeInteractiveSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SeServiceSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SePrincipalSelfSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SeLocalSystemSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SeAuthenticatedUserSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SeRestrictedCodeSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SeAliasAdminsSid = ExAllocatePoolWithTag(PagedPool, SidLength2, TAG_SID);
SeAliasUsersSid = ExAllocatePoolWithTag(PagedPool, SidLength2, TAG_SID);
SeAliasGuestsSid = ExAllocatePoolWithTag(PagedPool, SidLength2, TAG_SID);
SeAliasPowerUsersSid = ExAllocatePoolWithTag(PagedPool, SidLength2, TAG_SID);
SeAliasAccountOpsSid = ExAllocatePoolWithTag(PagedPool, SidLength2, TAG_SID);
SeAliasSystemOpsSid = ExAllocatePoolWithTag(PagedPool, SidLength2, TAG_SID);
SeAliasPrintOpsSid = ExAllocatePoolWithTag(PagedPool, SidLength2, TAG_SID);
SeAliasBackupOpsSid = ExAllocatePoolWithTag(PagedPool, SidLength2, TAG_SID);
SeAuthenticatedUsersSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SeRestrictedSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SeAnonymousLogonSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SeLocalServiceSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
SeNetworkServiceSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
if (SeNullSid == NULL || SeWorldSid == NULL ||
SeLocalSid == NULL || SeCreatorOwnerSid == NULL ||
SeCreatorGroupSid == NULL || SeCreatorOwnerServerSid == NULL ||
SeCreatorGroupServerSid == NULL || SeNtAuthoritySid == NULL ||
SeDialupSid == NULL || SeNetworkSid == NULL || SeBatchSid == NULL ||
SeInteractiveSid == NULL || SeServiceSid == NULL ||
SePrincipalSelfSid == NULL || SeLocalSystemSid == NULL ||
SeAuthenticatedUserSid == NULL || SeRestrictedCodeSid == NULL ||
SeAliasAdminsSid == NULL || SeAliasUsersSid == NULL ||
SeAliasGuestsSid == NULL || SeAliasPowerUsersSid == NULL ||
SeAliasAccountOpsSid == NULL || SeAliasSystemOpsSid == NULL ||
SeAliasPrintOpsSid == NULL || SeAliasBackupOpsSid == NULL ||
SeAuthenticatedUsersSid == NULL || SeRestrictedSid == NULL ||
SeAnonymousLogonSid == NULL || SeLocalServiceSid == NULL ||
SeNetworkServiceSid == NULL)
{
FreeInitializedSids();
return FALSE;
}
RtlInitializeSid(SeNullSid, &SeNullSidAuthority, 1);
RtlInitializeSid(SeWorldSid, &SeWorldSidAuthority, 1);
RtlInitializeSid(SeLocalSid, &SeLocalSidAuthority, 1);
RtlInitializeSid(SeCreatorOwnerSid, &SeCreatorSidAuthority, 1);
RtlInitializeSid(SeCreatorGroupSid, &SeCreatorSidAuthority, 1);
RtlInitializeSid(SeCreatorOwnerServerSid, &SeCreatorSidAuthority, 1);
RtlInitializeSid(SeCreatorGroupServerSid, &SeCreatorSidAuthority, 1);
RtlInitializeSid(SeNtAuthoritySid, &SeNtSidAuthority, 0);
RtlInitializeSid(SeDialupSid, &SeNtSidAuthority, 1);
RtlInitializeSid(SeNetworkSid, &SeNtSidAuthority, 1);
RtlInitializeSid(SeBatchSid, &SeNtSidAuthority, 1);
RtlInitializeSid(SeInteractiveSid, &SeNtSidAuthority, 1);
RtlInitializeSid(SeServiceSid, &SeNtSidAuthority, 1);
RtlInitializeSid(SePrincipalSelfSid, &SeNtSidAuthority, 1);
RtlInitializeSid(SeLocalSystemSid, &SeNtSidAuthority, 1);
RtlInitializeSid(SeAuthenticatedUserSid, &SeNtSidAuthority, 1);
RtlInitializeSid(SeRestrictedCodeSid, &SeNtSidAuthority, 1);
RtlInitializeSid(SeAliasAdminsSid, &SeNtSidAuthority, 2);
RtlInitializeSid(SeAliasUsersSid, &SeNtSidAuthority, 2);
RtlInitializeSid(SeAliasGuestsSid, &SeNtSidAuthority, 2);
RtlInitializeSid(SeAliasPowerUsersSid, &SeNtSidAuthority, 2);
RtlInitializeSid(SeAliasAccountOpsSid, &SeNtSidAuthority, 2);
RtlInitializeSid(SeAliasSystemOpsSid, &SeNtSidAuthority, 2);
RtlInitializeSid(SeAliasPrintOpsSid, &SeNtSidAuthority, 2);
RtlInitializeSid(SeAliasBackupOpsSid, &SeNtSidAuthority, 2);
RtlInitializeSid(SeAuthenticatedUsersSid, &SeNtSidAuthority, 1);
RtlInitializeSid(SeRestrictedSid, &SeNtSidAuthority, 1);
RtlInitializeSid(SeAnonymousLogonSid, &SeNtSidAuthority, 1);
RtlInitializeSid(SeLocalServiceSid, &SeNtSidAuthority, 1);
RtlInitializeSid(SeNetworkServiceSid, &SeNtSidAuthority, 1);
SubAuthority = RtlSubAuthoritySid(SeNullSid, 0);
*SubAuthority = SECURITY_NULL_RID;
SubAuthority = RtlSubAuthoritySid(SeWorldSid, 0);
*SubAuthority = SECURITY_WORLD_RID;
SubAuthority = RtlSubAuthoritySid(SeLocalSid, 0);
*SubAuthority = SECURITY_LOCAL_RID;
SubAuthority = RtlSubAuthoritySid(SeCreatorOwnerSid, 0);
*SubAuthority = SECURITY_CREATOR_OWNER_RID;
SubAuthority = RtlSubAuthoritySid(SeCreatorGroupSid, 0);
*SubAuthority = SECURITY_CREATOR_GROUP_RID;
SubAuthority = RtlSubAuthoritySid(SeCreatorOwnerServerSid, 0);
*SubAuthority = SECURITY_CREATOR_OWNER_SERVER_RID;
SubAuthority = RtlSubAuthoritySid(SeCreatorGroupServerSid, 0);
*SubAuthority = SECURITY_CREATOR_GROUP_SERVER_RID;
SubAuthority = RtlSubAuthoritySid(SeDialupSid, 0);
*SubAuthority = SECURITY_DIALUP_RID;
SubAuthority = RtlSubAuthoritySid(SeNetworkSid, 0);
*SubAuthority = SECURITY_NETWORK_RID;
SubAuthority = RtlSubAuthoritySid(SeBatchSid, 0);
*SubAuthority = SECURITY_BATCH_RID;
SubAuthority = RtlSubAuthoritySid(SeInteractiveSid, 0);
*SubAuthority = SECURITY_INTERACTIVE_RID;
SubAuthority = RtlSubAuthoritySid(SeServiceSid, 0);
*SubAuthority = SECURITY_SERVICE_RID;
SubAuthority = RtlSubAuthoritySid(SePrincipalSelfSid, 0);
*SubAuthority = SECURITY_PRINCIPAL_SELF_RID;
SubAuthority = RtlSubAuthoritySid(SeLocalSystemSid, 0);
*SubAuthority = SECURITY_LOCAL_SYSTEM_RID;
SubAuthority = RtlSubAuthoritySid(SeAuthenticatedUserSid, 0);
*SubAuthority = SECURITY_AUTHENTICATED_USER_RID;
SubAuthority = RtlSubAuthoritySid(SeRestrictedCodeSid, 0);
*SubAuthority = SECURITY_RESTRICTED_CODE_RID;
SubAuthority = RtlSubAuthoritySid(SeAliasAdminsSid, 0);
*SubAuthority = SECURITY_BUILTIN_DOMAIN_RID;
SubAuthority = RtlSubAuthoritySid(SeAliasAdminsSid, 1);
*SubAuthority = DOMAIN_ALIAS_RID_ADMINS;
SubAuthority = RtlSubAuthoritySid(SeAliasUsersSid, 0);
*SubAuthority = SECURITY_BUILTIN_DOMAIN_RID;
SubAuthority = RtlSubAuthoritySid(SeAliasUsersSid, 1);
*SubAuthority = DOMAIN_ALIAS_RID_USERS;
SubAuthority = RtlSubAuthoritySid(SeAliasGuestsSid, 0);
*SubAuthority = SECURITY_BUILTIN_DOMAIN_RID;
SubAuthority = RtlSubAuthoritySid(SeAliasGuestsSid, 1);
*SubAuthority = DOMAIN_ALIAS_RID_GUESTS;
SubAuthority = RtlSubAuthoritySid(SeAliasPowerUsersSid, 0);
*SubAuthority = SECURITY_BUILTIN_DOMAIN_RID;
SubAuthority = RtlSubAuthoritySid(SeAliasPowerUsersSid, 1);
*SubAuthority = DOMAIN_ALIAS_RID_POWER_USERS;
SubAuthority = RtlSubAuthoritySid(SeAliasAccountOpsSid, 0);
*SubAuthority = SECURITY_BUILTIN_DOMAIN_RID;
SubAuthority = RtlSubAuthoritySid(SeAliasAccountOpsSid, 1);
*SubAuthority = DOMAIN_ALIAS_RID_ACCOUNT_OPS;
SubAuthority = RtlSubAuthoritySid(SeAliasSystemOpsSid, 0);
*SubAuthority = SECURITY_BUILTIN_DOMAIN_RID;
SubAuthority = RtlSubAuthoritySid(SeAliasSystemOpsSid, 1);
*SubAuthority = DOMAIN_ALIAS_RID_SYSTEM_OPS;
SubAuthority = RtlSubAuthoritySid(SeAliasPrintOpsSid, 0);
*SubAuthority = SECURITY_BUILTIN_DOMAIN_RID;
SubAuthority = RtlSubAuthoritySid(SeAliasPrintOpsSid, 1);
*SubAuthority = DOMAIN_ALIAS_RID_PRINT_OPS;
SubAuthority = RtlSubAuthoritySid(SeAliasBackupOpsSid, 0);
*SubAuthority = SECURITY_BUILTIN_DOMAIN_RID;
SubAuthority = RtlSubAuthoritySid(SeAliasBackupOpsSid, 1);
*SubAuthority = DOMAIN_ALIAS_RID_BACKUP_OPS;
SubAuthority = RtlSubAuthoritySid(SeAuthenticatedUsersSid, 0);
*SubAuthority = SECURITY_AUTHENTICATED_USER_RID;
SubAuthority = RtlSubAuthoritySid(SeRestrictedSid, 0);
*SubAuthority = SECURITY_RESTRICTED_CODE_RID;
SubAuthority = RtlSubAuthoritySid(SeAnonymousLogonSid, 0);
*SubAuthority = SECURITY_ANONYMOUS_LOGON_RID;
SubAuthority = RtlSubAuthoritySid(SeLocalServiceSid, 0);
*SubAuthority = SECURITY_LOCAL_SERVICE_RID;
SubAuthority = RtlSubAuthoritySid(SeNetworkServiceSid, 0);
*SubAuthority = SECURITY_NETWORK_SERVICE_RID;
return TRUE;
}
/**
* @brief
* Captures a SID.
*
* @param[in] InputSid
* A valid security identifier to be captured.
*
* @param[in] AccessMode
* Processor level access mode.
*
* @param[in] PoolType
* Pool memory type for the captured SID to assign upon
* allocation.
*
* @param[in] CaptureIfKernel
* If set to TRUE, the capturing is done within the kernel.
* Otherwise the capturing is done in a kernel mode driver.
*
* @param[out] CapturedSid
* The captured security identifier, returned to the caller.
*
* @return
* Returns STATUS_SUCCESS if the SID was captured. STATUS_INSUFFICIENT_RESOURCES
* is returned if memory pool allocation for the captured SID has failed.
*/
NTSTATUS
NTAPI
SepCaptureSid(
_In_ PSID InputSid,
_In_ KPROCESSOR_MODE AccessMode,
_In_ POOL_TYPE PoolType,
_In_ BOOLEAN CaptureIfKernel,
_Out_ PSID *CapturedSid)
{
ULONG SidSize = 0;
PISID NewSid, Sid = (PISID)InputSid;
PAGED_CODE();
if (AccessMode != KernelMode)
{
_SEH2_TRY
{
ProbeForRead(Sid, FIELD_OFFSET(SID, SubAuthority), sizeof(UCHAR));
SidSize = RtlLengthRequiredSid(Sid->SubAuthorityCount);
ProbeForRead(Sid, SidSize, sizeof(UCHAR));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
/* Allocate a SID and copy it */
NewSid = ExAllocatePoolWithTag(PoolType, SidSize, TAG_SID);
if (!NewSid)
return STATUS_INSUFFICIENT_RESOURCES;
_SEH2_TRY
{
RtlCopyMemory(NewSid, Sid, SidSize);
*CapturedSid = NewSid;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Free the SID and return the exception code */
ExFreePoolWithTag(NewSid, TAG_SID);
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
else if (!CaptureIfKernel)
{
*CapturedSid = InputSid;
}
else
{
SidSize = RtlLengthRequiredSid(Sid->SubAuthorityCount);
/* Allocate a SID and copy it */
NewSid = ExAllocatePoolWithTag(PoolType, SidSize, TAG_SID);
if (NewSid == NULL)
return STATUS_INSUFFICIENT_RESOURCES;
RtlCopyMemory(NewSid, Sid, SidSize);
*CapturedSid = NewSid;
}
return STATUS_SUCCESS;
}
/**
* @brief
* Releases a captured SID.
*
* @param[in] CapturedSid
* The captured SID to be released.
*
* @param[in] AccessMode
* Processor level access mode.
*
* @param[in] CaptureIfKernel
* If set to TRUE, the releasing is done within the kernel.
* Otherwise the releasing is done in a kernel mode driver.
*
* @return
* Nothing.
*/
VOID
NTAPI
SepReleaseSid(
_In_ PSID CapturedSid,
_In_ KPROCESSOR_MODE AccessMode,
_In_ BOOLEAN CaptureIfKernel)
{
PAGED_CODE();
if (CapturedSid != NULL &&
(AccessMode != KernelMode ||
(AccessMode == KernelMode && CaptureIfKernel)))
{
ExFreePoolWithTag(CapturedSid, TAG_SID);
}
}
/**
* @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
* given access control entry. This identifier
* is valid for the whole of its lifetime.
*
* @param[in] AceType
* The type of an access control entry. This
* type that is given by the calling thread
* must coincide with the actual ACE that is
* given in the second parameter otherwise this
* can potentially lead to UNDEFINED behavior!
*
* @param[in] Ace
* A pointer to an access control entry, which
* can be obtained from a DACL.
*
* @return
* Returns a pointer to a security identifier (SID),
* otherwise NULL is returned if an unsupported ACE
* type was given to the function.
*/
PSID
NTAPI
SepGetSidFromAce(
_In_ UCHAR AceType,
_In_ PACE Ace)
{
PSID Sid;
PAGED_CODE();
/* Sanity check */
ASSERT(Ace);
/* Initialize the SID */
Sid = NULL;
/* Obtain the SID based upon ACE type */
switch (AceType)
{
case ACCESS_DENIED_ACE_TYPE:
{
Sid = (PSID)&((PACCESS_DENIED_ACE)Ace)->SidStart;
break;
}
case ACCESS_ALLOWED_ACE_TYPE:
{
Sid = (PSID)&((PACCESS_ALLOWED_ACE)Ace)->SidStart;
break;
}
case ACCESS_DENIED_OBJECT_ACE_TYPE:
{
Sid = (PSID)&((PACCESS_DENIED_OBJECT_ACE)Ace)->SidStart;
break;
}
case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
{
Sid = (PSID)&((PACCESS_ALLOWED_OBJECT_ACE)Ace)->SidStart;
break;
}
default:
break;
}
return Sid;
}
/**
* @brief
* Captures a SID with attributes.
*
* @param[in] SrcSidAndAttributes
* Source of the SID with attributes to be captured.
*
* @param[in] AttributeCount
* The number count of attributes, in total.
*
* @param[in] PreviousMode
* Processor access level mode.
*
* @param[in] AllocatedMem
* The allocated memory buffer for the captured SID. If the caller
* supplies no allocated block of memory then the function will
* allocate some buffer block of memory for the captured SID
* automatically.
*
* @param[in] AllocatedLength
* The length of the buffer that points to the allocated memory,
* in bytes.
*
* @param[in] PoolType
* The pool type for the captured SID and attributes to assign.
*
* @param[in] CaptureIfKernel
* If set to TRUE, the capturing is done within the kernel.
* Otherwise the capturing is done in a kernel mode driver.
*
* @param[out] CapturedSidAndAttributes
* The captured SID and attributes.
*
* @param[out] ResultLength
* The length of the captured SID and attributes, in bytes.
*
* @return
* Returns STATUS_SUCCESS if SID and attributes capturing
* has been completed successfully. STATUS_INVALID_PARAMETER
* is returned if the count of attributes exceeds the maximum
* threshold that the kernel can permit, with the purpose of
* avoiding integer overflows. STATUS_INSUFFICIENT_RESOURCES
* is returned if memory pool allocation for the captured SID has failed.
* STATUS_BUFFER_TOO_SMALL is returned if the length of the allocated
* buffer is less than the required size. STATUS_INVALID_SID is returned
* if a SID doesn't meet certain requirements to be considered a valid
* SID by the security manager. A failure NTSTATUS code is returned
* otherwise.
*
* @remarks
* A security identifier (SID) is a variable-length data structure that
* can change in size over time, depending on the factors that influence
* this effect in question. An attacker can take advantage of this fact
* and can potentially modify certain properties of a SID making it
* different in size than it was originally before. This is what we'd
* call, a TOCTOU vulnerability.
*
* For this reason, the logic of copying the SIDs and their attributes
* into a new buffer goes like this: first, allocate a buffer array
* that just holds the lengths and subauthority count of each SID.
* Such information is copied in the first loop. Then in a second loop,
* iterate over the array with SID provided and copy them into the final
* array. The moment we're doing this, validate the lengths of each SID
* basing upon the captured lengths we've got before. In this way we
* ensure that the SIDs have remained intact. The validation checks are
* done in user mode as a general rule that we just cannot trust UM and
* whatever data is coming from it.
*/
NTSTATUS
NTAPI
SeCaptureSidAndAttributesArray(
_In_ PSID_AND_ATTRIBUTES SrcSidAndAttributes,
_In_ ULONG AttributeCount,
_In_ KPROCESSOR_MODE PreviousMode,
_In_opt_ PVOID AllocatedMem,
_In_ ULONG AllocatedLength,
_In_ POOL_TYPE PoolType,
_In_ BOOLEAN CaptureIfKernel,
_Out_ PSID_AND_ATTRIBUTES *CapturedSidAndAttributes,
_Out_ PULONG ResultLength)
{
ULONG ArraySize, RequiredLength, SidLength, i;
ULONG TempArrayValidate, TempLengthValidate;
PSID_AND_ATTRIBUTES SidAndAttributes;
_SEH2_VOLATILE PSID_VALIDATE ValidateArray;
PUCHAR CurrentDest;
PISID Sid;
NTSTATUS Status;
PAGED_CODE();
ValidateArray = NULL;
SidAndAttributes = NULL;
*CapturedSidAndAttributes = NULL;
*ResultLength = 0;
if (AttributeCount == 0)
{
return STATUS_SUCCESS;
}
if (AttributeCount > SE_MAXIMUM_GROUP_LIMIT)
{
DPRINT1("SeCaptureSidAndAttributesArray(): Maximum group limit exceeded!\n");
return STATUS_INVALID_PARAMETER;
}
if ((PreviousMode == KernelMode) && !CaptureIfKernel)
{
*CapturedSidAndAttributes = SrcSidAndAttributes;
return STATUS_SUCCESS;
}
ArraySize = AttributeCount * sizeof(SID_AND_ATTRIBUTES);
RequiredLength = ALIGN_UP_BY(ArraySize, sizeof(ULONG));
if (PreviousMode != KernelMode)
{
/* Check for user mode data */
_SEH2_TRY
{
/* First probe the whole array */
ProbeForRead(SrcSidAndAttributes, ArraySize, sizeof(ULONG));
/* We're in user mode, set up the size for the temporary array */
TempArrayValidate = AttributeCount * sizeof(SID_VALIDATE);
TempLengthValidate = ALIGN_UP_BY(TempArrayValidate, sizeof(ULONG));
/*
* Allocate a buffer for the array that we're going to
* temporarily hold the subauthority count and the SID
* elements. We'll be going to use this array to perform
* validation checks later.
*/
ValidateArray = ExAllocatePoolWithTag(PoolType | POOL_RAISE_IF_ALLOCATION_FAILURE,
TempLengthValidate,
TAG_SID_VALIDATE);
/* Loop the array elements */
for (i = 0; i < AttributeCount; i++)
{
/* Get the SID and probe the minimal structure */
Sid = SrcSidAndAttributes[i].Sid;
ProbeForRead(Sid, sizeof(*Sid), sizeof(ULONG));
/*
* Capture the subauthority count and hold it
* into the temporary array for later validation.
* This way we ensure that the said count of each
* SID has remained the same.
*/
ValidateArray[i].SubAuthorityCount = Sid->SubAuthorityCount;
/* Capture the SID */
ValidateArray[i].ProbeSid = Sid;
/* Calculate the SID length and probe the full SID */
SidLength = RtlLengthRequiredSid(ValidateArray[i].SubAuthorityCount);
ProbeForRead(ValidateArray[i].ProbeSid, SidLength, sizeof(ULONG));
/* Add the aligned length to the required length */
RequiredLength += ALIGN_UP_BY(SidLength, sizeof(ULONG));
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(goto Cleanup);
}
_SEH2_END;
}
else
{
/* Loop the array elements */
for (i = 0; i < AttributeCount; i++)
{
/* Get the SID and it's length */
Sid = SrcSidAndAttributes[i].Sid;
SidLength = RtlLengthRequiredSid(Sid->SubAuthorityCount);
/* Add the aligned length to the required length */
RequiredLength += ALIGN_UP_BY(SidLength, sizeof(ULONG));
}
}
/* Assume success */
Status = STATUS_SUCCESS;
*ResultLength = RequiredLength;
/* Check if we have no buffer */
if (AllocatedMem == NULL)
{
/* Allocate a new buffer */
SidAndAttributes = ExAllocatePoolWithTag(PoolType,
RequiredLength,
TAG_SID_AND_ATTRIBUTES);
if (SidAndAttributes == NULL)
{
DPRINT1("SeCaptureSidAndAttributesArray(): Failed to allocate memory for SID and attributes array (requested size -> %lu)!\n", RequiredLength);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
}
/* Otherwise check if the buffer is large enough */
else if (AllocatedLength >= RequiredLength)
{
/* Buffer is large enough, use it */
SidAndAttributes = AllocatedMem;
}
else
{
/* Buffer is too small, fail */
DPRINT1("SeCaptureSidAndAttributesArray(): The provided buffer is small (expected size -> %lu || current size -> %lu)!\n", RequiredLength, AllocatedLength);
Status = STATUS_BUFFER_TOO_SMALL;
goto Cleanup;
}
*CapturedSidAndAttributes = SidAndAttributes;
/* Check again for user mode */
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
/* The rest of the data starts after the array */
CurrentDest = (PUCHAR)SidAndAttributes;
CurrentDest += ALIGN_UP_BY(ArraySize, sizeof(ULONG));
/* Loop the array elements */
for (i = 0; i < AttributeCount; i++)
{
/*
* Get the SID length from the subauthority
* count we've captured before.
*/
SidLength = RtlLengthRequiredSid(ValidateArray[i].SubAuthorityCount);
/* Copy attributes */
SidAndAttributes[i].Attributes = SrcSidAndAttributes[i].Attributes;
/* Copy the SID to the current destination address */
SidAndAttributes[i].Sid = (PSID)CurrentDest;
RtlCopyMemory(CurrentDest, ValidateArray[i].ProbeSid, SidLength);
/* Obtain the SID we've captured before for validation */
Sid = SidAndAttributes[i].Sid;
/* Validate that the subauthority count hasn't changed */
if (ValidateArray[i].SubAuthorityCount !=
Sid->SubAuthorityCount)
{
/* It's changed, bail out */
DPRINT1("SeCaptureSidAndAttributesArray(): The subauthority counts have changed (captured count -> %u || current count -> %u)\n",
ValidateArray[i].SubAuthorityCount, Sid->SubAuthorityCount);
Status = STATUS_INVALID_SID;
goto Cleanup;
}
/* Validate that the SID length is the same */
if (SidLength != RtlLengthSid(Sid))
{
/* They're no longer the same, bail out */
DPRINT1("SeCaptureSidAndAttributesArray(): The SID lengths have changed (captured length -> %lu || current length -> %lu)\n",
SidLength, RtlLengthSid(Sid));
Status = STATUS_INVALID_SID;
goto Cleanup;
}
/* Check that the SID is valid */
if (!RtlValidSid(Sid))
{
DPRINT1("SeCaptureSidAndAttributesArray(): The SID is not valid!\n");
Status = STATUS_INVALID_SID;
goto Cleanup;
}
/* Update the current destination address */
CurrentDest += ALIGN_UP_BY(SidLength, sizeof(ULONG));
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
else
{
/* The rest of the data starts after the array */
CurrentDest = (PUCHAR)SidAndAttributes;
CurrentDest += ALIGN_UP_BY(ArraySize, sizeof(ULONG));
/* Loop the array elements */
for (i = 0; i < AttributeCount; i++)
{
/* Get the SID and it's length */
Sid = SrcSidAndAttributes[i].Sid;
SidLength = RtlLengthRequiredSid(Sid->SubAuthorityCount);
/* Copy attributes */
SidAndAttributes[i].Attributes = SrcSidAndAttributes[i].Attributes;
/* Copy the SID to the current destination address */
SidAndAttributes[i].Sid = (PSID)CurrentDest;
RtlCopyMemory(CurrentDest, SrcSidAndAttributes[i].Sid, SidLength);
/* Update the current destination address */
CurrentDest += ALIGN_UP_BY(SidLength, sizeof(ULONG));
}
}
Cleanup:
/* Check for failure */
if (!NT_SUCCESS(Status))
{
/* Check if we allocated a new array */
if ((SidAndAttributes != AllocatedMem) && (SidAndAttributes != NULL))
{
/* Free the array */
ExFreePoolWithTag(SidAndAttributes, TAG_SID_AND_ATTRIBUTES);
}
/* Set returned address to NULL */
*CapturedSidAndAttributes = NULL;
}
/* Free the temporary validation array */
if ((PreviousMode != KernelMode) && (ValidateArray != NULL))
{
ExFreePoolWithTag(ValidateArray, TAG_SID_VALIDATE);
}
return Status;
}
/**
* @brief
* Releases a captured SID with attributes.
*
* @param[in] CapturedSidAndAttributes
* The captured SID with attributes to be released.
*
* @param[in] AccessMode
* Processor access level mode.
*
* @param[in] CaptureIfKernel
* If set to TRUE, the releasing is done within the kernel.
* Otherwise the releasing is done in a kernel mode driver.
*
* @return
* Nothing.
*/
VOID
NTAPI
SeReleaseSidAndAttributesArray(
_In_ _Post_invalid_ PSID_AND_ATTRIBUTES CapturedSidAndAttributes,
_In_ KPROCESSOR_MODE AccessMode,
_In_ BOOLEAN CaptureIfKernel)
{
PAGED_CODE();
if ((CapturedSidAndAttributes != NULL) &&
((AccessMode != KernelMode) || CaptureIfKernel))
{
ExFreePoolWithTag(CapturedSidAndAttributes, TAG_SID_AND_ATTRIBUTES);
}
}
/* EOF */