windows-nt/Source/XPSP1/NT/base/ntos/se/tokenqry.c

2045 lines
57 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
Tokenqry.c
Abstract:
This module implements the QUERY function for the executive
token object.
Author:
Jim Kelly (JimK) 15-June-1990
Revision History:
--*/
#include "pch.h"
#pragma hdrstop
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,NtQueryInformationToken)
#pragma alloc_text(PAGE,SeQueryAuthenticationIdToken)
#pragma alloc_text(PAGE,SeQueryInformationToken)
#pragma alloc_text(PAGE,SeQuerySessionIdToken)
#endif
NTSTATUS
NtQueryInformationToken (
IN HANDLE TokenHandle,
IN TOKEN_INFORMATION_CLASS TokenInformationClass,
OUT PVOID TokenInformation,
IN ULONG TokenInformationLength,
OUT PULONG ReturnLength
)
/*++
Routine Description:
Retrieve information about a specified token.
Arguments:
TokenHandle - Provides a handle to the token to operate on.
TokenInformationClass - The token information class about which
to retrieve information.
TokenInformation - The buffer to receive the requested class of
information. The buffer must be aligned on at least a
longword boundary. The actual structures returned are
dependent upon the information class requested, as defined in
the TokenInformationClass parameter description.
TokenInformation Format By Information Class:
TokenUser => TOKEN_USER data structure. TOKEN_QUERY
access is needed to retrieve this information about a
token.
TokenGroups => TOKEN_GROUPS data structure. TOKEN_QUERY
access is needed to retrieve this information about a
token.
TokenPrivileges => TOKEN_PRIVILEGES data structure.
TOKEN_QUERY access is needed to retrieve this information
about a token.
TokenOwner => TOKEN_OWNER data structure. TOKEN_QUERY
access is needed to retrieve this information about a
token.
TokenPrimaryGroup => TOKEN_PRIMARY_GROUP data structure.
TOKEN_QUERY access is needed to retrieve this information
about a token.
TokenDefaultDacl => TOKEN_DEFAULT_DACL data structure.
TOKEN_QUERY access is needed to retrieve this information
about a token.
TokenSource => TOKEN_SOURCE data structure.
TOKEN_QUERY_SOURCE access is needed to retrieve this
information about a token.
TokenType => TOKEN_TYPE data structure.
TOKEN_QUERY access is needed to retrieve this information
about a token.
TokenStatistics => TOKEN_STATISTICS data structure.
TOKEN_QUERY access is needed to retrieve this
information about a token.
TokenGroups => TOKEN_GROUPS data structure. TOKEN_QUERY
access is needed to retrieve this information about a
token.
TokenInformationLength - Indicates the length, in bytes, of the
TokenInformation buffer.
ReturnLength - This OUT parameter receives the actual length of
the requested information. If this value is larger than that
provided by the TokenInformationLength parameter, then the
buffer provided to receive the requested information is not
large enough to hold that data and no data is returned.
If the queried class is TokenDefaultDacl and there is no
default Dacl established for the token, then the return
length will be returned as zero, and no data will be returned.
Return Value:
STATUS_SUCCESS - Indicates the operation was successful.
STATUS_BUFFER_TOO_SMALL - if the requested information did not
fit in the provided output buffer. In this case, the
ReturnLength OUT parameter contains the number of bytes
actually needed to store the requested information.
--*/
{
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status;
PTOKEN Token;
ULONG RequiredLength;
ULONG Index;
ULONG GroupsLength;
ULONG RestrictedSidsLength;
ULONG PrivilegesLength;
PTOKEN_TYPE LocalType;
PTOKEN_USER LocalUser;
PTOKEN_GROUPS LocalGroups;
PTOKEN_PRIVILEGES LocalPrivileges;
PTOKEN_OWNER LocalOwner;
PTOKEN_PRIMARY_GROUP LocalPrimaryGroup;
PTOKEN_DEFAULT_DACL LocalDefaultDacl;
PTOKEN_SOURCE LocalSource;
PSECURITY_IMPERSONATION_LEVEL LocalImpersonationLevel;
PTOKEN_STATISTICS LocalStatistics;
PTOKEN_GROUPS_AND_PRIVILEGES LocalGroupsAndPrivileges;
PSID PSid;
PACL PAcl;
PVOID Ignore;
ULONG SessionId;
PAGED_CODE();
//
// Get previous processor mode and probe output argument if necessary.
//
PreviousMode = KeGetPreviousMode();
if (PreviousMode != KernelMode) {
try {
ProbeForWrite(
TokenInformation,
TokenInformationLength,
sizeof(ULONG)
);
ProbeForWriteUlong(ReturnLength);
} except(EXCEPTION_EXECUTE_HANDLER) {
return GetExceptionCode();
}
}
//
// Case on information class.
//
switch ( TokenInformationClass ) {
case TokenUser:
LocalUser = (PTOKEN_USER)TokenInformation;
Status = ObReferenceObjectByHandle(
TokenHandle, // Handle
TOKEN_QUERY, // DesiredAccess
SeTokenObjectType, // ObjectType
PreviousMode, // AccessMode
(PVOID *)&Token, // Object
NULL // GrantedAccess
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
//
// Gain exclusive access to the token.
//
SepAcquireTokenReadLock( Token );
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
RequiredLength = SeLengthSid( Token->UserAndGroups[0].Sid) +
(ULONG)sizeof( TOKEN_USER );
try {
*ReturnLength = RequiredLength;
} except(EXCEPTION_EXECUTE_HANDLER) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return GetExceptionCode();
}
if ( TokenInformationLength < RequiredLength ) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return STATUS_BUFFER_TOO_SMALL;
}
//
// Return the user SID
//
try {
//
// Put SID immediately following TOKEN_USER data structure
//
PSid = (PSID)( (ULONG_PTR)LocalUser + (ULONG)sizeof(TOKEN_USER) );
RtlCopySidAndAttributesArray(
1,
Token->UserAndGroups,
RequiredLength,
&(LocalUser->User),
PSid,
((PSID *)&Ignore),
((PULONG)&Ignore)
);
} except(EXCEPTION_EXECUTE_HANDLER) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return GetExceptionCode();
}
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return STATUS_SUCCESS;
case TokenGroups:
LocalGroups = (PTOKEN_GROUPS)TokenInformation;
Status = ObReferenceObjectByHandle(
TokenHandle, // Handle
TOKEN_QUERY, // DesiredAccess
SeTokenObjectType, // ObjectType
PreviousMode, // AccessMode
(PVOID *)&Token, // Object
NULL // GrantedAccess
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
Index = 1;
//
// Gain exclusive access to the token.
//
SepAcquireTokenReadLock( Token );
//
// Figure out how much space is needed to return the group SIDs.
// That's the size of TOKEN_GROUPS (without any array entries)
// plus the size of an SID_AND_ATTRIBUTES times the number of groups.
// The number of groups is Token->UserAndGroups-1 (since the count
// includes the user ID). Then the lengths of each individual group
// must be added.
//
RequiredLength = (ULONG)sizeof(TOKEN_GROUPS) +
((Token->UserAndGroupCount - ANYSIZE_ARRAY - 1) *
((ULONG)sizeof(SID_AND_ATTRIBUTES)) );
while (Index < Token->UserAndGroupCount) {
RequiredLength += SeLengthSid( Token->UserAndGroups[Index].Sid );
Index += 1;
} // endwhile
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
try {
*ReturnLength = RequiredLength;
} except(EXCEPTION_EXECUTE_HANDLER) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return GetExceptionCode();
}
if ( TokenInformationLength < RequiredLength ) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return STATUS_BUFFER_TOO_SMALL;
}
//
// Now copy the groups.
//
try {
LocalGroups->GroupCount = Token->UserAndGroupCount - 1;
PSid = (PSID)( (ULONG_PTR)LocalGroups +
(ULONG)sizeof(TOKEN_GROUPS) +
( (Token->UserAndGroupCount - ANYSIZE_ARRAY - 1) *
(ULONG)sizeof(SID_AND_ATTRIBUTES) )
);
RtlCopySidAndAttributesArray(
(ULONG)(Token->UserAndGroupCount - 1),
&(Token->UserAndGroups[1]),
RequiredLength,
LocalGroups->Groups,
PSid,
((PSID *)&Ignore),
((PULONG)&Ignore)
);
} except(EXCEPTION_EXECUTE_HANDLER) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return GetExceptionCode();
}
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return STATUS_SUCCESS;
case TokenRestrictedSids:
LocalGroups = (PTOKEN_GROUPS)TokenInformation;
Status = ObReferenceObjectByHandle(
TokenHandle, // Handle
TOKEN_QUERY, // DesiredAccess
SeTokenObjectType, // ObjectType
PreviousMode, // AccessMode
(PVOID *)&Token, // Object
NULL // GrantedAccess
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
Index = 0;
//
// Gain exclusive access to the token.
//
SepAcquireTokenReadLock( Token );
//
// Figure out how much space is needed to return the group SIDs.
// That's the size of TOKEN_GROUPS (without any array entries)
// plus the size of an SID_AND_ATTRIBUTES times the number of groups.
// The number of groups is Token->UserAndGroups-1 (since the count
// includes the user ID). Then the lengths of each individual group
// must be added.
//
RequiredLength = (ULONG)sizeof(TOKEN_GROUPS) +
((Token->RestrictedSidCount) *
((ULONG)sizeof(SID_AND_ATTRIBUTES)) -
ANYSIZE_ARRAY * sizeof(SID_AND_ATTRIBUTES) );
while (Index < Token->RestrictedSidCount) {
RequiredLength += SeLengthSid( Token->RestrictedSids[Index].Sid );
Index += 1;
} // endwhile
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
try {
*ReturnLength = RequiredLength;
} except(EXCEPTION_EXECUTE_HANDLER) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return GetExceptionCode();
}
if ( TokenInformationLength < RequiredLength ) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return STATUS_BUFFER_TOO_SMALL;
}
//
// Now copy the groups.
//
try {
LocalGroups->GroupCount = Token->RestrictedSidCount;
PSid = (PSID)( (ULONG_PTR)LocalGroups +
(ULONG)sizeof(TOKEN_GROUPS) +
( (Token->RestrictedSidCount ) *
(ULONG)sizeof(SID_AND_ATTRIBUTES) -
ANYSIZE_ARRAY * sizeof(SID_AND_ATTRIBUTES) )
);
RtlCopySidAndAttributesArray(
(ULONG)(Token->RestrictedSidCount),
Token->RestrictedSids,
RequiredLength,
LocalGroups->Groups,
PSid,
((PSID *)&Ignore),
((PULONG)&Ignore)
);
} except(EXCEPTION_EXECUTE_HANDLER) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return GetExceptionCode();
}
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return STATUS_SUCCESS;
case TokenPrivileges:
LocalPrivileges = (PTOKEN_PRIVILEGES)TokenInformation;
Status = ObReferenceObjectByHandle(
TokenHandle, // Handle
TOKEN_QUERY, // DesiredAccess
SeTokenObjectType, // ObjectType
PreviousMode, // AccessMode
(PVOID *)&Token, // Object
NULL // GrantedAccess
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
//
// Gain exclusive access to the token to prevent changes
// from occuring to the privileges.
//
SepAcquireTokenReadLock( Token );
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
RequiredLength = (ULONG)sizeof(TOKEN_PRIVILEGES) +
((Token->PrivilegeCount - ANYSIZE_ARRAY) *
((ULONG)sizeof(LUID_AND_ATTRIBUTES)) );
try {
*ReturnLength = RequiredLength;
} except(EXCEPTION_EXECUTE_HANDLER) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return GetExceptionCode();
}
if ( TokenInformationLength < RequiredLength ) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return STATUS_BUFFER_TOO_SMALL;
}
//
// Return the token privileges.
//
try {
LocalPrivileges->PrivilegeCount = Token->PrivilegeCount;
RtlCopyLuidAndAttributesArray(
Token->PrivilegeCount,
Token->Privileges,
LocalPrivileges->Privileges
);
} except(EXCEPTION_EXECUTE_HANDLER) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return GetExceptionCode();
}
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return STATUS_SUCCESS;
case TokenOwner:
LocalOwner = (PTOKEN_OWNER)TokenInformation;
Status = ObReferenceObjectByHandle(
TokenHandle, // Handle
TOKEN_QUERY, // DesiredAccess
SeTokenObjectType, // ObjectType
PreviousMode, // AccessMode
(PVOID *)&Token, // Object
NULL // GrantedAccess
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
//
// Gain exclusive access to the token to prevent changes
// from occuring to the owner.
//
SepAcquireTokenReadLock( Token );
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
PSid = Token->UserAndGroups[Token->DefaultOwnerIndex].Sid;
RequiredLength = (ULONG)sizeof(TOKEN_OWNER) +
SeLengthSid( PSid );
try {
*ReturnLength = RequiredLength;
} except(EXCEPTION_EXECUTE_HANDLER) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return GetExceptionCode();
}
if ( TokenInformationLength < RequiredLength ) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return STATUS_BUFFER_TOO_SMALL;
}
//
// Return the owner SID
//
PSid = (PSID)((ULONG_PTR)LocalOwner +
(ULONG)sizeof(TOKEN_OWNER));
try {
LocalOwner->Owner = PSid;
Status = RtlCopySid(
(RequiredLength - (ULONG)sizeof(TOKEN_OWNER)),
PSid,
Token->UserAndGroups[Token->DefaultOwnerIndex].Sid
);
ASSERT( NT_SUCCESS(Status) );
} except(EXCEPTION_EXECUTE_HANDLER) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return GetExceptionCode();
}
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return STATUS_SUCCESS;
case TokenPrimaryGroup:
LocalPrimaryGroup = (PTOKEN_PRIMARY_GROUP)TokenInformation;
Status = ObReferenceObjectByHandle(
TokenHandle, // Handle
TOKEN_QUERY, // DesiredAccess
SeTokenObjectType, // ObjectType
PreviousMode, // AccessMode
(PVOID *)&Token, // Object
NULL // GrantedAccess
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
//
// Gain exclusive access to the token to prevent changes
// from occuring to the owner.
//
SepAcquireTokenReadLock( Token );
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
RequiredLength = (ULONG)sizeof(TOKEN_PRIMARY_GROUP) +
SeLengthSid( Token->PrimaryGroup );
try {
*ReturnLength = RequiredLength;
} except(EXCEPTION_EXECUTE_HANDLER) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return GetExceptionCode();
}
if ( TokenInformationLength < RequiredLength ) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return STATUS_BUFFER_TOO_SMALL;
}
//
// Return the primary group SID
//
PSid = (PSID)((ULONG_PTR)LocalPrimaryGroup +
(ULONG)sizeof(TOKEN_PRIMARY_GROUP));
try {
LocalPrimaryGroup->PrimaryGroup = PSid;
Status = RtlCopySid( (RequiredLength - (ULONG)sizeof(TOKEN_PRIMARY_GROUP)),
PSid,
Token->PrimaryGroup
);
ASSERT( NT_SUCCESS(Status) );
} except(EXCEPTION_EXECUTE_HANDLER) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return GetExceptionCode();
}
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return STATUS_SUCCESS;
case TokenDefaultDacl:
LocalDefaultDacl = (PTOKEN_DEFAULT_DACL)TokenInformation;
Status = ObReferenceObjectByHandle(
TokenHandle, // Handle
TOKEN_QUERY, // DesiredAccess
SeTokenObjectType, // ObjectType
PreviousMode, // AccessMode
(PVOID *)&Token, // Object
NULL // GrantedAccess
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
RequiredLength = (ULONG)sizeof(TOKEN_DEFAULT_DACL);
//
// Gain exclusive access to the token to prevent changes
// from occuring to the owner.
//
SepAcquireTokenReadLock( Token );
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
if (ARGUMENT_PRESENT(Token->DefaultDacl)) {
RequiredLength += Token->DefaultDacl->AclSize;
}
try {
*ReturnLength = RequiredLength;
} except(EXCEPTION_EXECUTE_HANDLER) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return GetExceptionCode();
}
if ( TokenInformationLength < RequiredLength ) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return STATUS_BUFFER_TOO_SMALL;
}
//
// Return the default Dacl
//
PAcl = (PACL)((ULONG_PTR)LocalDefaultDacl +
(ULONG)sizeof(TOKEN_DEFAULT_DACL));
try {
if (ARGUMENT_PRESENT(Token->DefaultDacl)) {
LocalDefaultDacl->DefaultDacl = PAcl;
RtlCopyMemory( (PVOID)PAcl,
(PVOID)Token->DefaultDacl,
Token->DefaultDacl->AclSize
);
} else {
LocalDefaultDacl->DefaultDacl = NULL;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return GetExceptionCode();
}
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return STATUS_SUCCESS;
case TokenSource:
LocalSource = (PTOKEN_SOURCE)TokenInformation;
Status = ObReferenceObjectByHandle(
TokenHandle, // Handle
TOKEN_QUERY_SOURCE, // DesiredAccess
SeTokenObjectType, // ObjectType
PreviousMode, // AccessMode
(PVOID *)&Token, // Object
NULL // GrantedAccess
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
//
// The type of a token can not be changed, so
// exclusive access to the token is not necessary.
//
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
RequiredLength = (ULONG) sizeof(TOKEN_SOURCE);
try {
*ReturnLength = RequiredLength;
} except(EXCEPTION_EXECUTE_HANDLER) {
ObDereferenceObject( Token );
return GetExceptionCode();
}
if ( TokenInformationLength < RequiredLength ) {
ObDereferenceObject( Token );
return STATUS_BUFFER_TOO_SMALL;
}
//
// Return the token source
//
try {
(*LocalSource) = Token->TokenSource;
} except(EXCEPTION_EXECUTE_HANDLER) {
ObDereferenceObject( Token );
return GetExceptionCode();
}
ObDereferenceObject( Token );
return STATUS_SUCCESS;
case TokenType:
LocalType = (PTOKEN_TYPE)TokenInformation;
Status = ObReferenceObjectByHandle(
TokenHandle, // Handle
TOKEN_QUERY, // DesiredAccess
SeTokenObjectType, // ObjectType
PreviousMode, // AccessMode
(PVOID *)&Token, // Object
NULL // GrantedAccess
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
//
// The type of a token can not be changed, so
// exclusive access to the token is not necessary.
//
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
RequiredLength = (ULONG) sizeof(TOKEN_TYPE);
try {
*ReturnLength = RequiredLength;
} except(EXCEPTION_EXECUTE_HANDLER) {
ObDereferenceObject( Token );
return GetExceptionCode();
}
if ( TokenInformationLength < RequiredLength ) {
ObDereferenceObject( Token );
return STATUS_BUFFER_TOO_SMALL;
}
//
// Return the token type
//
try {
(*LocalType) = Token->TokenType;
} except(EXCEPTION_EXECUTE_HANDLER) {
ObDereferenceObject( Token );
return GetExceptionCode();
}
ObDereferenceObject( Token );
return STATUS_SUCCESS;
case TokenImpersonationLevel:
LocalImpersonationLevel = (PSECURITY_IMPERSONATION_LEVEL)TokenInformation;
Status = ObReferenceObjectByHandle(
TokenHandle, // Handle
TOKEN_QUERY, // DesiredAccess
SeTokenObjectType, // ObjectType
PreviousMode, // AccessMode
(PVOID *)&Token, // Object
NULL // GrantedAccess
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
//
// The impersonation level of a token can not be changed, so
// exclusive access to the token is not necessary.
//
//
// Make sure the token is an appropriate type to be retrieving
// the impersonation level from.
//
if (Token->TokenType != TokenImpersonation) {
ObDereferenceObject( Token );
return STATUS_INVALID_INFO_CLASS;
}
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
RequiredLength = (ULONG) sizeof(SECURITY_IMPERSONATION_LEVEL);
try {
*ReturnLength = RequiredLength;
} except(EXCEPTION_EXECUTE_HANDLER) {
ObDereferenceObject( Token );
return GetExceptionCode();
}
if ( TokenInformationLength < RequiredLength ) {
ObDereferenceObject( Token );
return STATUS_BUFFER_TOO_SMALL;
}
//
// Return the impersonation level
//
try {
(*LocalImpersonationLevel) = Token->ImpersonationLevel;
} except(EXCEPTION_EXECUTE_HANDLER) {
ObDereferenceObject( Token );
return GetExceptionCode();
}
ObDereferenceObject( Token );
return STATUS_SUCCESS;
case TokenStatistics:
LocalStatistics = (PTOKEN_STATISTICS)TokenInformation;
Status = ObReferenceObjectByHandle(
TokenHandle, // Handle
TOKEN_QUERY, // DesiredAccess
SeTokenObjectType, // ObjectType
PreviousMode, // AccessMode
(PVOID *)&Token, // Object
NULL // GrantedAccess
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
RequiredLength = (ULONG)sizeof( TOKEN_STATISTICS );
//
// Gain exclusive access to the token.
//
SepAcquireTokenReadLock( Token );
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
try {
*ReturnLength = RequiredLength;
} except(EXCEPTION_EXECUTE_HANDLER) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return GetExceptionCode();
}
if ( TokenInformationLength < RequiredLength ) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return STATUS_BUFFER_TOO_SMALL;
}
//
// Return the statistics
//
try {
ULONG Size;
LocalStatistics->TokenId = Token->TokenId;
LocalStatistics->AuthenticationId = Token->AuthenticationId;
LocalStatistics->ExpirationTime = Token->ExpirationTime;
LocalStatistics->TokenType = Token->TokenType;
LocalStatistics->ImpersonationLevel = Token->ImpersonationLevel;
LocalStatistics->DynamicCharged = Token->DynamicCharged;
Size = Token->DynamicCharged - SeLengthSid( Token->PrimaryGroup );;
if (Token->DefaultDacl) {
Size -= Token->DefaultDacl->AclSize;
}
LocalStatistics->DynamicAvailable = Size;
LocalStatistics->GroupCount = Token->UserAndGroupCount-1;
LocalStatistics->PrivilegeCount = Token->PrivilegeCount;
LocalStatistics->ModifiedId = Token->ModifiedId;
} except(EXCEPTION_EXECUTE_HANDLER) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return GetExceptionCode();
}
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return STATUS_SUCCESS;
case TokenSessionId:
if ( TokenInformationLength != sizeof(ULONG) )
return( STATUS_INFO_LENGTH_MISMATCH );
Status = ObReferenceObjectByHandle(
TokenHandle, // Handle
TOKEN_QUERY, // DesiredAccess
SeTokenObjectType, // ObjectType
PreviousMode, // AccessMode
(PVOID *)&Token, // Object
NULL // GrantedAccess
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
//
// Get SessionId for the token
//
SeQuerySessionIdToken( (PACCESS_TOKEN)Token,
&SessionId);
try {
*(PULONG)TokenInformation = SessionId;
*ReturnLength = sizeof(ULONG);
} except(EXCEPTION_EXECUTE_HANDLER) {
ObDereferenceObject( Token );
return GetExceptionCode();
}
ObDereferenceObject( Token );
return( STATUS_SUCCESS );
case TokenGroupsAndPrivileges:
LocalGroupsAndPrivileges = (PTOKEN_GROUPS_AND_PRIVILEGES)TokenInformation;
Status = ObReferenceObjectByHandle(
TokenHandle, // Handle
TOKEN_QUERY, // DesiredAccess
SeTokenObjectType, // ObjectType
PreviousMode, // AccessMode
(PVOID *)&Token, // Object
NULL // GrantedAccess
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
//
// Gain exclusive access to the token.
//
SepAcquireTokenReadLock( Token );
//
// Figure out how much space is needed to return the group SIDs.
// The data arrangement is as follows:
// GroupsAndPrivileges struct
// User and Groups
// Restricted sids
// Privileges
//
PrivilegesLength = Token->PrivilegeCount *
((ULONG)sizeof(LUID_AND_ATTRIBUTES));
GroupsLength = Token->UserAndGroupCount *
((ULONG)sizeof(SID_AND_ATTRIBUTES));
RestrictedSidsLength = Token->RestrictedSidCount *
((ULONG)sizeof(SID_AND_ATTRIBUTES));
Index = 0;
while (Index < Token->UserAndGroupCount) {
GroupsLength += SeLengthSid( Token->UserAndGroups[Index].Sid );
Index += 1;
} // endwhile
Index = 0;
while (Index < Token->RestrictedSidCount) {
RestrictedSidsLength += SeLengthSid( Token->RestrictedSids[Index].Sid );
Index += 1;
} // endwhile
RequiredLength = (ULONG)sizeof(TOKEN_GROUPS_AND_PRIVILEGES) +
PrivilegesLength + RestrictedSidsLength + GroupsLength;
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
try {
*ReturnLength = RequiredLength;
} except(EXCEPTION_EXECUTE_HANDLER) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return GetExceptionCode();
}
if ( TokenInformationLength < RequiredLength ) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return STATUS_BUFFER_TOO_SMALL;
}
//
// Now copy the groups, followed by restricted sids, followed by
// privileges.
//
try {
LocalGroupsAndPrivileges->AuthenticationId = Token->AuthenticationId;
LocalGroupsAndPrivileges->SidLength = GroupsLength;
LocalGroupsAndPrivileges->SidCount = Token->UserAndGroupCount;
LocalGroupsAndPrivileges->Sids = (PSID_AND_ATTRIBUTES) ((ULONG_PTR)LocalGroupsAndPrivileges +
(ULONG)sizeof(TOKEN_GROUPS_AND_PRIVILEGES));
LocalGroupsAndPrivileges->RestrictedSidLength = RestrictedSidsLength;
LocalGroupsAndPrivileges->RestrictedSidCount = Token->RestrictedSidCount;
//
// To distinguish between a restricted token with zero sids and
// a non-restrcited token.
//
if (SeTokenIsRestricted((PACCESS_TOKEN) Token))
{
LocalGroupsAndPrivileges->RestrictedSids = (PSID_AND_ATTRIBUTES) ((ULONG_PTR) LocalGroupsAndPrivileges->Sids +
GroupsLength);
}
else
{
LocalGroupsAndPrivileges->RestrictedSids = NULL;
}
LocalGroupsAndPrivileges->PrivilegeLength = PrivilegesLength;
LocalGroupsAndPrivileges->PrivilegeCount = Token->PrivilegeCount;
LocalGroupsAndPrivileges->Privileges = (PLUID_AND_ATTRIBUTES) ((ULONG_PTR) LocalGroupsAndPrivileges->Sids + GroupsLength +
RestrictedSidsLength);
PSid = (PSID)( (ULONG_PTR)LocalGroupsAndPrivileges->Sids +
(Token->UserAndGroupCount *
(ULONG)sizeof(SID_AND_ATTRIBUTES))
);
RtlCopySidAndAttributesArray(
(ULONG)Token->UserAndGroupCount,
Token->UserAndGroups,
GroupsLength - (Token->UserAndGroupCount * ((ULONG)sizeof(SID_AND_ATTRIBUTES))),
LocalGroupsAndPrivileges->Sids,
PSid,
((PSID *)&Ignore),
((PULONG)&Ignore)
);
PSid = (PSID)((ULONG_PTR)LocalGroupsAndPrivileges->RestrictedSids +
((Token->RestrictedSidCount ) *
(ULONG)sizeof(SID_AND_ATTRIBUTES))
);
if (LocalGroupsAndPrivileges->RestrictedSidCount > 0)
{
RtlCopySidAndAttributesArray(
(ULONG)(Token->RestrictedSidCount),
Token->RestrictedSids,
RestrictedSidsLength - (Token->RestrictedSidCount * ((ULONG)sizeof(SID_AND_ATTRIBUTES))),
LocalGroupsAndPrivileges->RestrictedSids,
PSid,
((PSID *)&Ignore),
((PULONG)&Ignore)
);
}
RtlCopyLuidAndAttributesArray(
Token->PrivilegeCount,
Token->Privileges,
LocalGroupsAndPrivileges->Privileges
);
} except(EXCEPTION_EXECUTE_HANDLER) {
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return GetExceptionCode();
}
SepReleaseTokenReadLock( Token );
ObDereferenceObject( Token );
return STATUS_SUCCESS;
case TokenSandBoxInert:
try {
*ReturnLength = sizeof(ULONG);
} except(EXCEPTION_EXECUTE_HANDLER) {
return GetExceptionCode();
}
if ( TokenInformationLength < sizeof(ULONG) ) {
return( STATUS_INFO_LENGTH_MISMATCH );
}
Status = ObReferenceObjectByHandle(
TokenHandle, // Handle
TOKEN_QUERY, // DesiredAccess
SeTokenObjectType, // ObjectType
PreviousMode, // AccessMode
(PVOID *)&Token, // Object
NULL // GrantedAccess
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
try {
//
// If the flag is present in the token then return TRUE.
// Else return FALSE.
//
*(PULONG)TokenInformation = (Token->TokenFlags & TOKEN_SANDBOX_INERT)
? TRUE : FALSE;
} except(EXCEPTION_EXECUTE_HANDLER) {
ObDereferenceObject( Token );
return GetExceptionCode();
}
ObDereferenceObject( Token );
return( STATUS_SUCCESS );
default:
return STATUS_INVALID_INFO_CLASS;
}
}
NTSTATUS
SeQueryAuthenticationIdToken(
IN PACCESS_TOKEN Token,
OUT PLUID AuthenticationId
)
/*++
Routine Description:
Retrieve authentication ID out of the token.
Arguments:
Token - Referenced pointer to a token.
AutenticationId - Receives the token's authentication ID.
Return Value:
STATUS_SUCCESS - Indicates the operation was successful.
This is the only expected status.
--*/
{
PAGED_CODE();
SepAcquireTokenReadLock( ((PTOKEN)Token) );
(*AuthenticationId) = ((PTOKEN)Token)->AuthenticationId;
SepReleaseTokenReadLock( ((PTOKEN)Token) );
return(STATUS_SUCCESS);
}
NTSTATUS
SeQueryInformationToken (
IN PACCESS_TOKEN AccessToken,
IN TOKEN_INFORMATION_CLASS TokenInformationClass,
OUT PVOID *TokenInformation
)
/*++
Routine Description:
Retrieve information about a specified token.
Arguments:
TokenHandle - Provides a handle to the token to operate on.
TokenInformationClass - The token information class about which
to retrieve information.
TokenInformation - Receives a pointer to the requested information.
The actual structures returned are dependent upon the information
class requested, as defined in the TokenInformationClass parameter
description.
TokenInformation Format By Information Class:
TokenUser => TOKEN_USER data structure. TOKEN_QUERY
access is needed to retrieve this information about a
token.
TokenGroups => TOKEN_GROUPS data structure. TOKEN_QUERY
access is needed to retrieve this information about a
token.
TokenPrivileges => TOKEN_PRIVILEGES data structure.
TOKEN_QUERY access is needed to retrieve this information
about a token.
TokenOwner => TOKEN_OWNER data structure. TOKEN_QUERY
access is needed to retrieve this information about a
token.
TokenPrimaryGroup => TOKEN_PRIMARY_GROUP data structure.
TOKEN_QUERY access is needed to retrieve this information
about a token.
TokenDefaultDacl => TOKEN_DEFAULT_DACL data structure.
TOKEN_QUERY access is needed to retrieve this information
about a token.
TokenSource => TOKEN_SOURCE data structure.
TOKEN_QUERY_SOURCE access is needed to retrieve this
information about a token.
TokenType => TOKEN_TYPE data structure.
TOKEN_QUERY access is needed to retrieve this information
about a token.
TokenStatistics => TOKEN_STATISTICS data structure.
TOKEN_QUERY access is needed to retrieve this
information about a token.
Return Value:
STATUS_SUCCESS - Indicates the operation was successful.
--*/
{
NTSTATUS Status;
ULONG RequiredLength;
ULONG Index;
PSID PSid;
PACL PAcl;
PVOID Ignore;
PTOKEN Token = (PTOKEN)AccessToken;
PAGED_CODE();
//
// Case on information class.
//
switch ( TokenInformationClass ) {
case TokenUser:
{
PTOKEN_USER LocalUser;
//
// Gain exclusive access to the token.
//
SepAcquireTokenReadLock( Token );
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
RequiredLength = SeLengthSid( Token->UserAndGroups[0].Sid) +
(ULONG)sizeof( TOKEN_USER );
LocalUser = ExAllocatePool( PagedPool, RequiredLength );
if (LocalUser == NULL) {
SepReleaseTokenReadLock( Token );
return( STATUS_INSUFFICIENT_RESOURCES );
}
//
// Return the user SID
//
// Put SID immediately following TOKEN_USER data structure
//
PSid = (PSID)( (ULONG_PTR)LocalUser + (ULONG)sizeof(TOKEN_USER) );
RtlCopySidAndAttributesArray(
1,
Token->UserAndGroups,
RequiredLength,
&(LocalUser->User),
PSid,
((PSID *)&Ignore),
((PULONG)&Ignore)
);
SepReleaseTokenReadLock( Token );
*TokenInformation = LocalUser;
return STATUS_SUCCESS;
}
case TokenGroups:
{
PTOKEN_GROUPS LocalGroups;
//
// Gain exclusive access to the token.
//
SepAcquireTokenReadLock( Token );
//
// Figure out how much space is needed to return the group SIDs.
// That's the size of TOKEN_GROUPS (without any array entries)
// plus the size of an SID_AND_ATTRIBUTES times the number of groups.
// The number of groups is Token->UserAndGroups-1 (since the count
// includes the user ID). Then the lengths of each individual group
// must be added.
//
RequiredLength = (ULONG)sizeof(TOKEN_GROUPS) +
((Token->UserAndGroupCount - ANYSIZE_ARRAY - 1) *
((ULONG)sizeof(SID_AND_ATTRIBUTES)) );
Index = 1;
while (Index < Token->UserAndGroupCount) {
RequiredLength += SeLengthSid( Token->UserAndGroups[Index].Sid );
Index += 1;
} // endwhile
LocalGroups = ExAllocatePool( PagedPool, RequiredLength );
if (LocalGroups == NULL) {
SepReleaseTokenReadLock( Token );
return( STATUS_INSUFFICIENT_RESOURCES );
}
//
// Now copy the groups.
//
LocalGroups->GroupCount = Token->UserAndGroupCount - 1;
PSid = (PSID)( (ULONG_PTR)LocalGroups +
(ULONG)sizeof(TOKEN_GROUPS) +
( (Token->UserAndGroupCount - ANYSIZE_ARRAY - 1) *
(ULONG)sizeof(SID_AND_ATTRIBUTES) )
);
RtlCopySidAndAttributesArray(
(ULONG)(Token->UserAndGroupCount - 1),
&(Token->UserAndGroups[1]),
RequiredLength,
LocalGroups->Groups,
PSid,
((PSID *)&Ignore),
((PULONG)&Ignore)
);
SepReleaseTokenReadLock( Token );
*TokenInformation = LocalGroups;
return STATUS_SUCCESS;
}
case TokenPrivileges:
{
PTOKEN_PRIVILEGES LocalPrivileges;
//
// Gain exclusive access to the token to prevent changes
// from occuring to the privileges.
//
SepAcquireTokenReadLock( Token );
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
RequiredLength = (ULONG)sizeof(TOKEN_PRIVILEGES) +
((Token->PrivilegeCount - ANYSIZE_ARRAY) *
((ULONG)sizeof(LUID_AND_ATTRIBUTES)) );
LocalPrivileges = ExAllocatePool( PagedPool, RequiredLength );
if (LocalPrivileges == NULL) {
SepReleaseTokenReadLock( Token );
return( STATUS_INSUFFICIENT_RESOURCES );
}
//
// Return the token privileges.
//
LocalPrivileges->PrivilegeCount = Token->PrivilegeCount;
RtlCopyLuidAndAttributesArray(
Token->PrivilegeCount,
Token->Privileges,
LocalPrivileges->Privileges
);
SepReleaseTokenReadLock( Token );
*TokenInformation = LocalPrivileges;
return STATUS_SUCCESS;
}
case TokenOwner:
{
PTOKEN_OWNER LocalOwner;
//
// Gain exclusive access to the token to prevent changes
// from occuring to the owner.
//
SepAcquireTokenReadLock( Token );
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
PSid = Token->UserAndGroups[Token->DefaultOwnerIndex].Sid;
RequiredLength = (ULONG)sizeof(TOKEN_OWNER) +
SeLengthSid( PSid );
LocalOwner = ExAllocatePool( PagedPool, RequiredLength );
if (LocalOwner == NULL) {
SepReleaseTokenReadLock( Token );
return( STATUS_INSUFFICIENT_RESOURCES );
}
//
// Return the owner SID
//
PSid = (PSID)((ULONG_PTR)LocalOwner +
(ULONG)sizeof(TOKEN_OWNER));
LocalOwner->Owner = PSid;
Status = RtlCopySid(
(RequiredLength - (ULONG)sizeof(TOKEN_OWNER)),
PSid,
Token->UserAndGroups[Token->DefaultOwnerIndex].Sid
);
ASSERT( NT_SUCCESS(Status) );
SepReleaseTokenReadLock( Token );
*TokenInformation = LocalOwner;
return STATUS_SUCCESS;
}
case TokenPrimaryGroup:
{
PTOKEN_PRIMARY_GROUP LocalPrimaryGroup;
//
// Gain exclusive access to the token to prevent changes
// from occuring to the owner.
//
SepAcquireTokenReadLock( Token );
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
RequiredLength = (ULONG)sizeof(TOKEN_PRIMARY_GROUP) +
SeLengthSid( Token->PrimaryGroup );
LocalPrimaryGroup = ExAllocatePool( PagedPool, RequiredLength );
if (LocalPrimaryGroup == NULL) {
SepReleaseTokenReadLock( Token );
return( STATUS_INSUFFICIENT_RESOURCES );
}
//
// Return the primary group SID
//
PSid = (PSID)((ULONG_PTR)LocalPrimaryGroup +
(ULONG)sizeof(TOKEN_PRIMARY_GROUP));
LocalPrimaryGroup->PrimaryGroup = PSid;
Status = RtlCopySid( (RequiredLength - (ULONG)sizeof(TOKEN_PRIMARY_GROUP)),
PSid,
Token->PrimaryGroup
);
ASSERT( NT_SUCCESS(Status) );
SepReleaseTokenReadLock( Token );
*TokenInformation = LocalPrimaryGroup;
return STATUS_SUCCESS;
}
case TokenDefaultDacl:
{
PTOKEN_DEFAULT_DACL LocalDefaultDacl;
RequiredLength = (ULONG)sizeof(TOKEN_DEFAULT_DACL);
//
// Gain exclusive access to the token to prevent changes
// from occuring to the owner.
//
SepAcquireTokenReadLock( Token );
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
if (ARGUMENT_PRESENT(Token->DefaultDacl)) {
RequiredLength += Token->DefaultDacl->AclSize;
}
LocalDefaultDacl = ExAllocatePool( PagedPool, RequiredLength );
if (LocalDefaultDacl == NULL) {
SepReleaseTokenReadLock( Token );
return( STATUS_INSUFFICIENT_RESOURCES );
}
//
// Return the default Dacl
//
PAcl = (PACL)((ULONG_PTR)LocalDefaultDacl +
(ULONG)sizeof(TOKEN_DEFAULT_DACL));
if (ARGUMENT_PRESENT(Token->DefaultDacl)) {
LocalDefaultDacl->DefaultDacl = PAcl;
RtlCopyMemory( (PVOID)PAcl,
(PVOID)Token->DefaultDacl,
Token->DefaultDacl->AclSize
);
} else {
LocalDefaultDacl->DefaultDacl = NULL;
}
SepReleaseTokenReadLock( Token );
*TokenInformation = LocalDefaultDacl;
return STATUS_SUCCESS;
}
case TokenSource:
{
PTOKEN_SOURCE LocalSource;
//
// The type of a token can not be changed, so
// exclusive access to the token is not necessary.
//
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
RequiredLength = (ULONG) sizeof(TOKEN_SOURCE);
LocalSource = ExAllocatePool( PagedPool, RequiredLength );
if (LocalSource == NULL) {
return( STATUS_INSUFFICIENT_RESOURCES );
}
//
// Return the token source
//
(*LocalSource) = Token->TokenSource;
*TokenInformation = LocalSource;
return STATUS_SUCCESS;
}
case TokenType:
{
PTOKEN_TYPE LocalType;
//
// The type of a token can not be changed, so
// exclusive access to the token is not necessary.
//
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
RequiredLength = (ULONG) sizeof(TOKEN_TYPE);
LocalType = ExAllocatePool( PagedPool, RequiredLength );
if (LocalType == NULL) {
return( STATUS_INSUFFICIENT_RESOURCES );
}
//
// Return the token type
//
(*LocalType) = Token->TokenType;
*TokenInformation = LocalType;
return STATUS_SUCCESS;
}
case TokenImpersonationLevel:
{
PSECURITY_IMPERSONATION_LEVEL LocalImpersonationLevel;
//
// The impersonation level of a token can not be changed, so
// exclusive access to the token is not necessary.
//
//
// Make sure the token is an appropriate type to be retrieving
// the impersonation level from.
//
if (Token->TokenType != TokenImpersonation) {
return STATUS_INVALID_INFO_CLASS;
}
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
RequiredLength = (ULONG) sizeof(SECURITY_IMPERSONATION_LEVEL);
LocalImpersonationLevel = ExAllocatePool( PagedPool, RequiredLength );
if (LocalImpersonationLevel == NULL) {
return( STATUS_INSUFFICIENT_RESOURCES );
}
//
// Return the impersonation level
//
(*LocalImpersonationLevel) = Token->ImpersonationLevel;
*TokenInformation = LocalImpersonationLevel;
return STATUS_SUCCESS;
}
case TokenStatistics:
{
PTOKEN_STATISTICS LocalStatistics;
ULONG Size;
//
// Return the length required now in case not enough buffer
// was provided by the caller and we have to return an error.
//
RequiredLength = (ULONG)sizeof( TOKEN_STATISTICS );
LocalStatistics = ExAllocatePool( PagedPool, RequiredLength );
if (LocalStatistics == NULL) {
return( STATUS_INSUFFICIENT_RESOURCES );
}
//
// Gain exclusive access to the token.
//
SepAcquireTokenReadLock( Token );
//
// Return the statistics
//
LocalStatistics->TokenId = Token->TokenId;
LocalStatistics->AuthenticationId = Token->AuthenticationId;
LocalStatistics->ExpirationTime = Token->ExpirationTime;
LocalStatistics->TokenType = Token->TokenType;
LocalStatistics->ImpersonationLevel = Token->ImpersonationLevel;
LocalStatistics->DynamicCharged = Token->DynamicCharged;
Size = Token->DynamicCharged - SeLengthSid( Token->PrimaryGroup );
if (Token->DefaultDacl) {
Size -= Token->DefaultDacl->AclSize;
}
LocalStatistics->DynamicAvailable = Size;
LocalStatistics->DynamicAvailable = Token->DynamicAvailable;
LocalStatistics->GroupCount = Token->UserAndGroupCount-1;
LocalStatistics->PrivilegeCount = Token->PrivilegeCount;
LocalStatistics->ModifiedId = Token->ModifiedId;
SepReleaseTokenReadLock( Token );
*TokenInformation = LocalStatistics;
return STATUS_SUCCESS;
}
case TokenSessionId:
/*
* Get SessionId for the token
*/
SeQuerySessionIdToken( (PACCESS_TOKEN)Token,
(PULONG)TokenInformation );
return( STATUS_SUCCESS );
default:
return STATUS_INVALID_INFO_CLASS;
}
}
NTSTATUS
SeQuerySessionIdToken(
PACCESS_TOKEN Token,
PULONG SessionId
)
/*++
Routine Description:
Gets the SessionId from the specified token object.
Arguments:
Token (input)
Opaque kernel ACCESS_TOKEN pointer
SessionId (output)
pointer to location to return SessionId
Return Value:
STATUS_SUCCESS - no error
--*/
{
PAGED_CODE();
/*
* Get the SessionId.
*/
SepAcquireTokenReadLock( ((PTOKEN)Token) );
(*SessionId) = ((PTOKEN)Token)->SessionId;
SepReleaseTokenReadLock( ((PTOKEN)Token) );
return( STATUS_SUCCESS );
}