/*
* Copyright (c) Likewise Software. All rights Reserved.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the license, or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details. You should have received a copy
* of the GNU Lesser General Public License along with this program. If
* not, see .
*
* LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
* TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
* WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
* TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
* LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU
* HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
* TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
* license@likewisesoftware.com
*/
/*
* Module Name:
*
* security-token.c
*
* Abstract:
*
* Token/Access Functions in Security Module.
*
* Authors: Danilo Almeida (dalmeida@likewise.com)
*
*/
#include "security-includes.h"
#include
//
// ACCESS_MASK Functions
//
static
inline
VOID
RtlpMapGenericMaskSingleFlag(
IN OUT PACCESS_MASK AccessMask,
IN ACCESS_MASK GenericFlag,
IN ACCESS_MASK SpecificFlags
)
{
if (IsSetFlag(*AccessMask, GenericFlag))
{
ClearFlag(*AccessMask, GenericFlag);
SetFlag(*AccessMask, SpecificFlags);
}
}
VOID
RtlMapGenericMask(
IN OUT PACCESS_MASK AccessMask,
IN PGENERIC_MAPPING GenericMapping
)
{
RtlpMapGenericMaskSingleFlag(AccessMask, GENERIC_READ, GenericMapping->GenericRead);
RtlpMapGenericMaskSingleFlag(AccessMask, GENERIC_WRITE, GenericMapping->GenericWrite);
RtlpMapGenericMaskSingleFlag(AccessMask, GENERIC_EXECUTE, GenericMapping->GenericExecute);
RtlpMapGenericMaskSingleFlag(AccessMask, GENERIC_ALL, GenericMapping->GenericAll);
}
//
// Access Token Functions
//
inline
static
PVOID
RtlpAppendData(
IN PVOID Location,
IN PVOID Data,
IN ULONG DataSize
)
{
RtlCopyMemory(Location, Data, DataSize);
return LW_PTR_ADD(Location, DataSize);
}
NTSTATUS
RtlCreateAccessToken(
OUT PACCESS_TOKEN* AccessToken,
IN PTOKEN_USER User,
IN PTOKEN_GROUPS Groups,
IN PTOKEN_OWNER Owner,
IN PTOKEN_PRIMARY_GROUP PrimaryGroup,
IN PTOKEN_DEFAULT_DACL DefaultDacl,
IN OPTIONAL PTOKEN_UNIX Unix
)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG requiredSize = 0;
PACCESS_TOKEN token = NULL;
ULONG i = 0;
ULONG size = 0;
PVOID location = NULL;
if (!User || !User->User.Sid ||
!Groups ||
!Owner ||
!PrimaryGroup ||
!DefaultDacl)
{
status = STATUS_INVALID_PARAMETER;
GOTO_CLEANUP();
}
if (!RtlValidSid(User->User.Sid) ||
(Owner->Owner && !RtlValidSid(Owner->Owner)) ||
(PrimaryGroup->PrimaryGroup && !RtlValidSid(PrimaryGroup->PrimaryGroup)))
{
status = STATUS_INVALID_SID;
GOTO_CLEANUP();
}
// No user attributes currently exist.
if (User->User.Attributes != 0)
{
status = STATUS_INVALID_PARAMETER;
GOTO_CLEANUP();
}
for (i = 0; i < Groups->GroupCount; i++)
{
// TODO-Perhaps validate Group attributes
if (!Groups->Groups[i].Sid)
{
status = STATUS_INVALID_PARAMETER;
GOTO_CLEANUP();
}
if (!RtlValidSid(Groups->Groups[i].Sid))
{
status = STATUS_INVALID_SID;
GOTO_CLEANUP();
}
}
if (DefaultDacl->DefaultDacl &&
!RtlValidAcl(DefaultDacl->DefaultDacl, NULL))
{
status = STATUS_INVALID_ACL;
GOTO_CLEANUP();
}
// Compute size required
requiredSize = sizeof(*token);
size = RtlLengthSid(User->User.Sid);
status = RtlSafeAddULONG(&requiredSize, requiredSize, size);
GOTO_CLEANUP_ON_STATUS(status);
if (Owner->Owner)
{
size = RtlLengthSid(Owner->Owner);
status = RtlSafeAddULONG(&requiredSize, requiredSize, size);
GOTO_CLEANUP_ON_STATUS(status);
}
if (PrimaryGroup->PrimaryGroup)
{
size = RtlLengthSid(PrimaryGroup->PrimaryGroup);
status = RtlSafeAddULONG(&requiredSize, requiredSize, size);
GOTO_CLEANUP_ON_STATUS(status);
}
if (DefaultDacl->DefaultDacl)
{
status = RtlSafeAddULONG(&requiredSize, requiredSize,
DefaultDacl->DefaultDacl->AclSize);
GOTO_CLEANUP_ON_STATUS(status);
}
status = RtlSafeMultiplyULONG(&size, sizeof(Groups->Groups[0]), Groups->GroupCount);
GOTO_CLEANUP_ON_STATUS(status);
status = RtlSafeAddULONG(&requiredSize, requiredSize, size);
GOTO_CLEANUP_ON_STATUS(status);
for (i = 0; i < Groups->GroupCount; i++)
{
size = RtlLengthSid(Groups->Groups[i].Sid);
status = RtlSafeAddULONG(&requiredSize, requiredSize, size);
GOTO_CLEANUP_ON_STATUS(status);
}
status = RTL_ALLOCATE(&token, ACCESS_TOKEN, requiredSize);
GOTO_CLEANUP_ON_STATUS(status);
location = LW_PTR_ADD(token, sizeof(*token));
// Initialize
token->ReferenceCount = 1;
token->Flags = 0;
token->User.Attributes = User->User.Attributes;
token->User.Sid = (PSID) location;
location = RtlpAppendData(location,
User->User.Sid,
RtlLengthSid(User->User.Sid));
token->GroupCount = Groups->GroupCount;
token->Groups = (PSID_AND_ATTRIBUTES) location;
location = LwRtlOffsetToPointer(location, sizeof(Groups->Groups[0]) * Groups->GroupCount);
for (i = 0; i < Groups->GroupCount; i++)
{
token->Groups[i].Attributes = Groups->Groups[i].Attributes;
token->Groups[i].Sid = (PSID) location;
location = RtlpAppendData(location,
Groups->Groups[i].Sid,
RtlLengthSid(Groups->Groups[i].Sid));
}
if (Owner->Owner)
{
token->Owner = (PSID) location;
location = RtlpAppendData(location,
Owner->Owner,
RtlLengthSid(Owner->Owner));
}
if (PrimaryGroup->PrimaryGroup)
{
token->PrimaryGroup = (PSID) location;
location = RtlpAppendData(location,
PrimaryGroup->PrimaryGroup,
RtlLengthSid(PrimaryGroup->PrimaryGroup));
}
if (DefaultDacl->DefaultDacl)
{
token->DefaultDacl = (PACL) location;
location = RtlpAppendData(location,
DefaultDacl->DefaultDacl,
DefaultDacl->DefaultDacl->AclSize);
}
if (Unix)
{
SetFlag(token->Flags, ACCESS_TOKEN_FLAG_UNIX_PRESENT);
token->Uid = Unix->Uid;
token->Gid = Unix->Gid;
token->Umask = Unix->Umask;
}
if (location != LW_PTR_ADD(token, requiredSize))
{
status = STATUS_ASSERTION_FAILURE;
GOTO_CLEANUP();
}
status = STATUS_SUCCESS;
cleanup:
if (!NT_SUCCESS(status))
{
RtlReleaseAccessToken(&token);
}
*AccessToken = token;
return status;
}
VOID
RtlReferenceAccessToken(
IN PACCESS_TOKEN AccessToken
)
{
LwInterlockedIncrement(&AccessToken->ReferenceCount);
}
VOID
RtlReleaseAccessToken(
IN OUT PACCESS_TOKEN* AccessToken
)
{
PACCESS_TOKEN accessToken = *AccessToken;
if (accessToken)
{
LONG count = LwInterlockedDecrement(&accessToken->ReferenceCount);
assert(count >= 0);
if (0 == count)
{
RTL_FREE(AccessToken);
}
}
}
NTSTATUS
RtlQueryAccessTokenInformation(
IN PACCESS_TOKEN AccessToken,
IN TOKEN_INFORMATION_CLASS TokenInformationClass,
OUT OPTIONAL PVOID TokenInformation,
IN ULONG TokenInformationLength,
OUT PULONG ReturnedLength
)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG requiredSize = 0;
ULONG i = 0;
PVOID location = NULL;
if (!AccessToken || !ReturnedLength)
{
status = STATUS_INVALID_PARAMETER;
GOTO_CLEANUP();
}
if (!TokenInformation && (TokenInformationLength != 0))
{
status = STATUS_INVALID_PARAMETER;
GOTO_CLEANUP();
}
// Check required size
switch (TokenInformationClass)
{
case TokenUser:
status = RtlSafeAddULONG(
&requiredSize,
sizeof(TOKEN_USER),
RtlLengthSid(AccessToken->User.Sid));
GOTO_CLEANUP_ON_STATUS(status);
break;
case TokenGroups:
status = RtlSafeMultiplyULONG(
&requiredSize,
sizeof(AccessToken->Groups[0]),
AccessToken->GroupCount);
GOTO_CLEANUP_ON_STATUS(status);
status = RtlSafeAddULONG(&requiredSize, requiredSize, sizeof(TOKEN_GROUPS));
GOTO_CLEANUP_ON_STATUS(status);
for (i = 0; i < AccessToken->GroupCount; i++)
{
status = RtlSafeAddULONG(
&requiredSize,
requiredSize,
RtlLengthSid(AccessToken->Groups[i].Sid));
GOTO_CLEANUP_ON_STATUS(status);
}
break;
case TokenOwner:
status = RtlSafeAddULONG(
&requiredSize,
sizeof(TOKEN_OWNER),
RtlLengthSid(AccessToken->Owner));
GOTO_CLEANUP_ON_STATUS(status);
break;
case TokenPrimaryGroup:
status = RtlSafeAddULONG(
&requiredSize,
sizeof(TOKEN_PRIMARY_GROUP),
RtlLengthSid(AccessToken->PrimaryGroup));
GOTO_CLEANUP_ON_STATUS(status);
break;
case TokenDefaultDacl:
status = RtlSafeAddULONG(
&requiredSize,
sizeof(TOKEN_DEFAULT_DACL),
(AccessToken->DefaultDacl ?
AccessToken->DefaultDacl->AclSize :
0));
GOTO_CLEANUP_ON_STATUS(status);
break;
default:
status = STATUS_INVALID_PARAMETER;
GOTO_CLEANUP();
}
if (requiredSize > TokenInformationLength)
{
status = STATUS_BUFFER_TOO_SMALL;
GOTO_CLEANUP();
}
if (!TokenInformation)
{
status = STATUS_ASSERTION_FAILURE;
GOTO_CLEANUP();
}
// Copy data
switch (TokenInformationClass)
{
case TokenUser:
{
PTOKEN_USER tokenInfo = (PTOKEN_USER) TokenInformation;
location = LW_PTR_ADD(TokenInformation, sizeof(TOKEN_USER));
tokenInfo->User.Attributes = AccessToken->User.Attributes;
tokenInfo->User.Sid = (PSID) location;
location = RtlpAppendData(location,
AccessToken->User.Sid,
RtlLengthSid(AccessToken->User.Sid));
break;
}
case TokenGroups:
{
PTOKEN_GROUPS tokenInfo = (PTOKEN_GROUPS) TokenInformation;
location = LW_PTR_ADD(TokenInformation,
(sizeof(TOKEN_GROUPS) +
(sizeof(AccessToken->Groups[0]) * AccessToken->GroupCount)));
tokenInfo->GroupCount = AccessToken->GroupCount;
for (i = 0; i < AccessToken->GroupCount; i++)
{
tokenInfo->Groups[i].Attributes = AccessToken->Groups[i].Attributes;
tokenInfo->Groups[i].Sid = (PSID) location;
location = RtlpAppendData(location,
AccessToken->Groups[i].Sid,
RtlLengthSid(AccessToken->Groups[i].Sid));
}
break;
}
case TokenOwner:
{
PTOKEN_OWNER tokenInfo = (PTOKEN_OWNER) TokenInformation;
location = LW_PTR_ADD(TokenInformation, sizeof(TOKEN_OWNER));
tokenInfo->Owner = (PSID) location;
location = RtlpAppendData(location,
AccessToken->Owner,
RtlLengthSid(AccessToken->Owner));
break;
}
case TokenPrimaryGroup:
{
PTOKEN_PRIMARY_GROUP tokenInfo = (PTOKEN_PRIMARY_GROUP) TokenInformation;
location = LW_PTR_ADD(TokenInformation, sizeof(TOKEN_PRIMARY_GROUP));
tokenInfo->PrimaryGroup = (PSID) location;
location = RtlpAppendData(location,
AccessToken->PrimaryGroup,
RtlLengthSid(AccessToken->PrimaryGroup));
break;
}
case TokenDefaultDacl:
{
PTOKEN_DEFAULT_DACL tokenInfo = (PTOKEN_DEFAULT_DACL) TokenInformation;
location = LW_PTR_ADD(TokenInformation, sizeof(TOKEN_DEFAULT_DACL));
if (AccessToken->DefaultDacl)
{
tokenInfo->DefaultDacl = (PACL) location;
location = RtlpAppendData(location,
AccessToken->DefaultDacl,
AccessToken->DefaultDacl->AclSize);
}
break;
}
default:
// We should have already checked.
status = STATUS_ASSERTION_FAILURE;
GOTO_CLEANUP();
}
if (location != LW_PTR_ADD(TokenInformation, requiredSize))
{
status = STATUS_ASSERTION_FAILURE;
GOTO_CLEANUP();
}
status = STATUS_SUCCESS;
cleanup:
*ReturnedLength = requiredSize;
return status;
}
NTSTATUS
RtlQueryAccessTokenUnixInformation(
IN PACCESS_TOKEN AccessToken,
OUT PTOKEN_UNIX TokenInformation
)
{
NTSTATUS status = STATUS_SUCCESS;
TOKEN_UNIX tokenInfo = { 0 };
if (!AccessToken)
{
status = STATUS_INVALID_PARAMETER;
GOTO_CLEANUP();
}
if (!IsSetFlag(AccessToken->Flags, ACCESS_TOKEN_FLAG_UNIX_PRESENT))
{
status = STATUS_NOT_FOUND;
GOTO_CLEANUP();
}
tokenInfo.Uid = AccessToken->Uid;
tokenInfo.Gid = AccessToken->Gid;
tokenInfo.Umask = AccessToken->Umask;
status = STATUS_SUCCESS;
cleanup:
if (!NT_SUCCESS(status))
{
RtlZeroMemory(&tokenInfo, sizeof(tokenInfo));
}
*TokenInformation = tokenInfo;
return status;
}
BOOLEAN
RtlIsSidMemberOfToken(
IN PACCESS_TOKEN AccessToken,
IN PSID Sid
)
{
BOOLEAN isMember = FALSE;
ULONG i = 0;
if (RtlEqualSid(Sid, AccessToken->User.Sid))
{
isMember = TRUE;
GOTO_CLEANUP();
}
for (i = 0; i < AccessToken->GroupCount; i++)
{
PSID_AND_ATTRIBUTES sidInfo = &AccessToken->Groups[i];
if (IsSetFlag(sidInfo->Attributes, SE_GROUP_ENABLED) &&
RtlEqualSid(Sid, sidInfo->Sid))
{
isMember = TRUE;
GOTO_CLEANUP();
}
}
isMember = FALSE;
cleanup:
return isMember;
}
BOOLEAN
RtlAccessCheck(
IN PSECURITY_DESCRIPTOR_ABSOLUTE SecurityDescriptor,
IN PACCESS_TOKEN AccessToken,
IN ACCESS_MASK DesiredAccess,
IN ACCESS_MASK PreviouslyGrantedAccess,
IN PGENERIC_MAPPING GenericMapping,
OUT PACCESS_MASK GrantedAccess,
OUT PNTSTATUS AccessStatus
)
{
NTSTATUS status = STATUS_ACCESS_DENIED;
ACCESS_MASK grantedAccess = PreviouslyGrantedAccess;
ACCESS_MASK desiredAccess = DesiredAccess;
BOOLEAN wantMaxAllowed = FALSE;
USHORT aclSizeUsed = 0;
USHORT aceOffset = 0;
PACE_HEADER aceHeader = NULL;
union {
SID Sid;
BYTE Buffer[SID_MAX_SIZE];
} sidBuffer;
ULONG ulSidSize = sizeof(sidBuffer);
if (!SecurityDescriptor || !AccessToken || !GenericMapping)
{
status = STATUS_INVALID_PARAMETER;
GOTO_CLEANUP();
}
if (!LW_IS_VALID_FLAGS(DesiredAccess, VALID_DESIRED_ACCESS_MASK))
{
status = STATUS_INVALID_PARAMETER;
GOTO_CLEANUP();
}
if (!LW_IS_VALID_FLAGS(PreviouslyGrantedAccess, VALID_GRANTED_ACCESS_MASK))
{
status = STATUS_INVALID_PARAMETER;
GOTO_CLEANUP();
}
if ((SecurityDescriptor->Owner == NULL) ||
(SecurityDescriptor->Group == NULL))
{
status = STATUS_INVALID_SECURITY_DESCR;
GOTO_CLEANUP();
}
wantMaxAllowed = IsSetFlag(desiredAccess, MAXIMUM_ALLOWED);
ClearFlag(desiredAccess, MAXIMUM_ALLOWED);
RtlMapGenericMask(&desiredAccess, GenericMapping);
//
// NT AUTHORITY\SYSTEM is always allowed an access
//
status = RtlCreateWellKnownSid(WinLocalSystemSid,
NULL,
&sidBuffer.Sid,
&ulSidSize);
GOTO_CLEANUP_ON_STATUS(status);
if (RtlIsSidMemberOfToken(AccessToken, &sidBuffer.Sid))
{
if (wantMaxAllowed)
{
SetFlag(desiredAccess, STANDARD_RIGHTS_ALL);
SetFlag(desiredAccess, GENERIC_ALL);
RtlMapGenericMask(&desiredAccess, GenericMapping);
}
SetFlag(grantedAccess, desiredAccess);
desiredAccess = 0;
status = STATUS_SUCCESS;
GOTO_CLEANUP();
}
if (IsSetFlag(desiredAccess, ACCESS_SYSTEM_SECURITY))
{
// TODO-Handle ACCESS_SYSTEM_SECURITY by checking SE_SECURITY_NAME
// privilege. For now, requesting ACCESS_SYSTEM_SECURITY is not
// allowed.
status = STATUS_PRIVILEGE_NOT_HELD;
GOTO_CLEANUP();
}
if (IsSetFlag(desiredAccess, WRITE_OWNER))
{
// TODO-Allow WRITE_OWNER if have SE_TAKE_OWNERSHIP_NAME regardless
// of DACL.
//
// BUILTIN\Administrators are always allowed WRITE_OWNER
//
ulSidSize = sizeof(sidBuffer);
status = RtlCreateWellKnownSid(
WinBuiltinAdministratorsSid,
NULL,
&sidBuffer.Sid,
&ulSidSize);
GOTO_CLEANUP_ON_STATUS(status);
if (RtlIsSidMemberOfToken(AccessToken, &sidBuffer.Sid))
{
SetFlag(grantedAccess, WRITE_OWNER);
ClearFlag(desiredAccess, WRITE_OWNER);
}
}
//
// Owner can always read the SD and write the DACL.
//
if (wantMaxAllowed || IsSetFlag(desiredAccess, READ_CONTROL | WRITE_DAC))
{
if (RtlIsSidMemberOfToken(AccessToken, SecurityDescriptor->Owner))
{
if (wantMaxAllowed)
{
desiredAccess |= (READ_CONTROL | WRITE_DAC);
}
SetFlag(grantedAccess, (READ_CONTROL | WRITE_DAC) & desiredAccess);
ClearFlag(desiredAccess, grantedAccess);
}
}
// TODO-MAXIMUM_ALLOWED wrt privileges and WRITE_OWNER and
// ACCESS_SYSTEM_SECURITY above.
if (!SecurityDescriptor->Dacl)
{
// TODO-Interplay with special bits above?
if (wantMaxAllowed)
{
SetFlag(desiredAccess, STANDARD_RIGHTS_ALL);
SetFlag(desiredAccess, GENERIC_ALL);
RtlMapGenericMask(&desiredAccess, GenericMapping);
}
SetFlag(grantedAccess, desiredAccess);
desiredAccess = 0;
status = STATUS_SUCCESS;
GOTO_CLEANUP();
}
if (!RtlValidAcl(SecurityDescriptor->Dacl, &aclSizeUsed))
{
status = STATUS_INVALID_ACL;
GOTO_CLEANUP();
}
while (wantMaxAllowed || desiredAccess)
{
status = RtlIterateAce(SecurityDescriptor->Dacl,
aclSizeUsed,
&aceOffset,
&aceHeader);
if (STATUS_NO_MORE_ENTRIES == status)
{
break;
}
GOTO_CLEANUP_ON_STATUS(status);
// Check ACE
switch (aceHeader->AceType)
{
case ACCESS_ALLOWED_ACE_TYPE:
{
PACCESS_ALLOWED_ACE ace = (PACCESS_ALLOWED_ACE) aceHeader;
ACCESS_MASK mask = ace->Mask;
RtlMapGenericMask(&mask, GenericMapping);
if (wantMaxAllowed || IsSetFlag(desiredAccess, mask))
{
// SID in token => add bits to granted bits
PSID sid = RtlpGetSidAccessAllowedAce(ace);
if (RtlIsSidMemberOfToken(AccessToken, sid))
{
if (wantMaxAllowed)
{
SetFlag(grantedAccess, mask);
}
else
{
SetFlag(grantedAccess, mask & desiredAccess);
}
ClearFlag(desiredAccess, grantedAccess);
}
}
break;
}
case ACCESS_DENIED_ACE_TYPE:
{
// Allowed and deny ACEs are isomorphic.
PACCESS_ALLOWED_ACE ace = (PACCESS_ALLOWED_ACE) aceHeader;
ACCESS_MASK mask = ace->Mask;
RtlMapGenericMask(&mask, GenericMapping);
if (IsSetFlag(desiredAccess, mask))
{
// SID in token => exit with STATUS_ACCESS_DENIED
PSID sid = RtlpGetSidAccessAllowedAce(ace);
if (RtlIsSidMemberOfToken(AccessToken, sid))
{
status = STATUS_ACCESS_DENIED;
GOTO_CLEANUP();
}
}
break;
}
default:
// ignore
break;
}
}
status = desiredAccess ? STATUS_ACCESS_DENIED : STATUS_SUCCESS;
cleanup:
if (NT_SUCCESS(status) &&
!LW_IS_VALID_FLAGS(grantedAccess, VALID_GRANTED_ACCESS_MASK))
{
status = STATUS_ASSERTION_FAILURE;
}
if (!NT_SUCCESS(status))
{
grantedAccess = PreviouslyGrantedAccess;
}
*GrantedAccess = grantedAccess;
*AccessStatus = status;
return NT_SUCCESS(status) ? TRUE : FALSE;
}
static
inline
VOID
Align32(
PULONG ulValue
)
{
ULONG ulRem = *ulValue % 32;
if (ulRem) *ulValue += (32 - ulRem);
}
static
inline
NTSTATUS
CheckOffset(
ULONG ulOffset,
ULONG ulSize,
ULONG ulMaxSize
)
{
if (ulMaxSize - ulOffset < ulSize)
{
return STATUS_BUFFER_TOO_SMALL;
}
else
{
return STATUS_SUCCESS;
}
}
static
inline
NTSTATUS
AdvanceTo(
PULONG ulValue,
ULONG ulPosition,
ULONG ulSize
)
{
if (ulPosition >= ulSize)
{
return STATUS_BUFFER_TOO_SMALL;
}
else
{
*ulValue = ulPosition;
return STATUS_SUCCESS;
}
}
static
ULONG
RtlAccessTokenRelativeSize(
PACCESS_TOKEN pToken
)
{
ULONG ulRelativeSize = 0;
ULONG i = 0;
ulRelativeSize += sizeof(ACCESS_TOKEN_SELF_RELATIVE);
Align32(&ulRelativeSize);
ulRelativeSize += RtlLengthSid(pToken->User.Sid);
Align32(&ulRelativeSize);
if (pToken->Groups);
{
ulRelativeSize += sizeof(SID_AND_ATTRIBUTES_SELF_RELATIVE) * pToken->GroupCount;
Align32(&ulRelativeSize);
for (i = 0; i < pToken->GroupCount; i++)
{
ulRelativeSize += RtlLengthSid(pToken->Groups[i].Sid);
Align32(&ulRelativeSize);
}
}
if (pToken->Owner)
{
ulRelativeSize += RtlLengthSid(pToken->Owner);
Align32(&ulRelativeSize);
}
if (pToken->PrimaryGroup)
{
ulRelativeSize += RtlLengthSid(pToken->PrimaryGroup);
Align32(&ulRelativeSize);
}
if (pToken->DefaultDacl)
{
ulRelativeSize += RtlGetAclSize(pToken->DefaultDacl);
Align32(&ulRelativeSize);
}
return ulRelativeSize;
}
NTSTATUS
RtlAccessTokenToSelfRelativeAccessToken(
IN PACCESS_TOKEN pToken,
OUT OPTIONAL PACCESS_TOKEN_SELF_RELATIVE pRelative,
IN OUT PULONG pulSize
)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG ulRelativeSize = RtlAccessTokenRelativeSize(pToken);
PSID_AND_ATTRIBUTES_SELF_RELATIVE pGroups = NULL;
PBYTE pBuffer = NULL;
ULONG ulOffset = 0;
ULONG i = 0;
if (pRelative)
{
if (*pulSize < ulRelativeSize)
{
status = STATUS_BUFFER_TOO_SMALL;
GOTO_CLEANUP_ON_STATUS(status);
}
pBuffer = (PBYTE) pRelative;
pRelative->Flags = pToken->Flags;
pRelative->User.Attributes = pToken->User.Attributes;
pRelative->GroupCount = pToken->GroupCount;
pRelative->Uid = pToken->Uid;
pRelative->Gid = pToken->Gid;
pRelative->Umask = pToken->Umask;
ulOffset += sizeof(*pRelative);
Align32(&ulOffset);
pRelative->User.SidOffset = ulOffset;
memcpy(pBuffer + ulOffset, pToken->User.Sid, RtlLengthSid(pToken->User.Sid));
ulOffset += RtlLengthSid(pToken->User.Sid);
Align32(&ulOffset);
if (pToken->Groups)
{
pRelative->GroupsOffset = ulOffset;
pGroups = (PSID_AND_ATTRIBUTES_SELF_RELATIVE) (pBuffer + ulOffset);
ulOffset += sizeof(SID_AND_ATTRIBUTES_SELF_RELATIVE) * pToken->GroupCount;
Align32(&ulOffset);
for (i = 0; i < pToken->GroupCount; i++)
{
pGroups[i].Attributes = pToken->Groups[i].Attributes;
pGroups[i].SidOffset = ulOffset;
memcpy(pBuffer + ulOffset, pToken->Groups[i].Sid, RtlLengthSid(pToken->Groups[i].Sid));
ulOffset += RtlLengthSid(pToken->Groups[i].Sid);
Align32(&ulOffset);
}
}
else
{
pRelative->GroupsOffset = 0;
}
if (pToken->Owner)
{
pRelative->OwnerOffset = ulOffset;
memcpy(pBuffer + ulOffset, pToken->Owner, RtlLengthSid(pToken->Owner));
ulOffset += RtlLengthSid(pToken->Owner);
Align32(&ulOffset);
}
else
{
pRelative->OwnerOffset = 0;
}
if (pToken->PrimaryGroup)
{
pRelative->PrimaryGroupOffset = ulOffset;
memcpy(pBuffer + ulOffset, pToken->PrimaryGroup, RtlLengthSid(pToken->PrimaryGroup));
ulOffset += RtlLengthSid(pToken->PrimaryGroup);
Align32(&ulOffset);
}
else
{
pRelative->PrimaryGroupOffset = 0;
}
if (pToken->DefaultDacl)
{
pRelative->DefaultDaclOffset = ulOffset;
memcpy(pBuffer + ulOffset, pToken->DefaultDacl, RtlGetAclSize(pToken->DefaultDacl));
ulOffset += RtlGetAclSize(pToken->DefaultDacl);
Align32(&ulOffset);
}
else
{
pRelative->DefaultDaclOffset = 0;
}
assert(ulOffset == ulRelativeSize);
}
cleanup:
*pulSize = ulRelativeSize;
return status;
}
static
NTSTATUS
RtlValidateSelfRelativeSid(
PSID pSid,
ULONG ulOffset,
ULONG ulRelativeSize
)
{
NTSTATUS status = STATUS_SUCCESS;
status = CheckOffset(ulOffset, SID_MIN_SIZE, ulRelativeSize);
GOTO_CLEANUP_ON_STATUS(status);
status = CheckOffset(ulOffset, RtlLengthSid(pSid), ulRelativeSize);
GOTO_CLEANUP_ON_STATUS(status);
cleanup:
return status;
}
NTSTATUS
RtlSelfRelativeAccessTokenToAccessToken(
IN PACCESS_TOKEN_SELF_RELATIVE pRelative,
IN ULONG ulRelativeSize,
OUT PACCESS_TOKEN* ppToken
)
{
NTSTATUS status = STATUS_SUCCESS;
PACCESS_TOKEN pToken = NULL;
ULONG ulOffset = 0;
PBYTE pBuffer = (PBYTE) pRelative;
PSID pSid = NULL;
PSID_AND_ATTRIBUTES_SELF_RELATIVE pGroups = NULL;
ULONG ulSize = 0;
ULONG ulRealSize = 0;
ULONG i = 0;
status = RTL_ALLOCATE(&pToken, ACCESS_TOKEN, sizeof(*pToken));
GOTO_CLEANUP_ON_STATUS(status);
pToken->ReferenceCount = 1;
status = CheckOffset(0, sizeof(*pRelative), ulRelativeSize);
GOTO_CLEANUP_ON_STATUS(status);
pToken->Flags = pRelative->Flags;
pToken->User.Attributes = pRelative->User.Attributes;
pToken->GroupCount = pRelative->GroupCount;
pToken->Uid = pRelative->Uid;
pToken->Gid = pRelative->Gid;
pToken->Umask = pRelative->Umask;
ulOffset = pRelative->User.SidOffset;
pSid = (PSID) (pBuffer + ulOffset);
status = RtlValidateSelfRelativeSid(pSid, ulOffset, ulRelativeSize);
GOTO_CLEANUP_ON_STATUS(status);
status = RtlDuplicateSid(&pToken->User.Sid, pSid);
GOTO_CLEANUP_ON_STATUS(status);
ulOffset = pRelative->GroupsOffset;
if (ulOffset)
{
status = LwRtlSafeMultiplyULONG(
&ulSize,
sizeof(SID_AND_ATTRIBUTES_SELF_RELATIVE),
pRelative->GroupCount);
GOTO_CLEANUP_ON_STATUS(status);
status = LwRtlSafeMultiplyULONG(
&ulRealSize,
sizeof(SID_AND_ATTRIBUTES),
pRelative->GroupCount);
GOTO_CLEANUP_ON_STATUS(status);
status = CheckOffset(ulOffset, ulSize, ulRelativeSize);
GOTO_CLEANUP_ON_STATUS(status);
pGroups = (PSID_AND_ATTRIBUTES_SELF_RELATIVE) (pBuffer + ulOffset);
status = RTL_ALLOCATE(&pToken->Groups, SID_AND_ATTRIBUTES, ulRealSize);
GOTO_CLEANUP_ON_STATUS(status);
for (i = 0; i < pRelative->GroupCount; i++)
{
pToken->Groups[i].Attributes = pGroups[i].Attributes;
ulOffset = pGroups[i].SidOffset;
pSid = (PSID) (pBuffer + ulOffset);
status = RtlValidateSelfRelativeSid(pSid, ulOffset, ulRelativeSize);
GOTO_CLEANUP_ON_STATUS(status);
status = RtlDuplicateSid(&pToken->Groups[i].Sid, pSid);
GOTO_CLEANUP_ON_STATUS(status);
}
}
ulOffset = pRelative->OwnerOffset;
if (ulOffset)
{
pSid = (PSID) (pBuffer + ulOffset);
status = RtlValidateSelfRelativeSid(pSid, ulOffset, ulRelativeSize);
GOTO_CLEANUP_ON_STATUS(status);
status = RtlDuplicateSid(&pToken->Owner, pSid);
GOTO_CLEANUP_ON_STATUS(status);
}
ulOffset = pRelative->PrimaryGroupOffset;
if (ulOffset)
{
pSid = (PSID) (pBuffer + ulOffset);
status = RtlValidateSelfRelativeSid(pSid, ulOffset, ulRelativeSize);
GOTO_CLEANUP_ON_STATUS(status);
status = RtlDuplicateSid(&pToken->PrimaryGroup, pSid);
GOTO_CLEANUP_ON_STATUS(status);
}
*ppToken = pToken;
cleanup:
if (!NT_SUCCESS(status))
{
RtlReleaseAccessToken(&pToken);
}
return status;
}
/*
local variables:
mode: c
c-basic-offset: 4
indent-tabs-mode: nil
tab-width: 4
end:
*/