/*++ 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 ); }