/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/se/token.c * PURPOSE: Security manager * * PROGRAMMERS: David Welch */ /* INCLUDES *******************************************************************/ #include #define NDEBUG #include #if defined (ALLOC_PRAGMA) #pragma alloc_text(INIT, SepInitializeTokenImplementation) #endif /* GLOBALS ********************************************************************/ POBJECT_TYPE SepTokenObjectType = NULL; ERESOURCE SepTokenLock; TOKEN_SOURCE SeSystemTokenSource = {"*SYSTEM*", {0}}; LUID SeSystemAuthenticationId = SYSTEM_LUID; static GENERIC_MAPPING SepTokenMapping = {TOKEN_READ, TOKEN_WRITE, TOKEN_EXECUTE, TOKEN_ALL_ACCESS}; static const INFORMATION_CLASS_INFO SeTokenInformationClass[] = { /* Class 0 not used, blame M$! */ ICI_SQ_SAME( 0, 0, 0), /* TokenUser */ ICI_SQ_SAME( sizeof(TOKEN_USER), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ), /* TokenGroups */ ICI_SQ_SAME( sizeof(TOKEN_GROUPS), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ), /* TokenPrivileges */ ICI_SQ_SAME( sizeof(TOKEN_PRIVILEGES), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ), /* TokenOwner */ ICI_SQ_SAME( sizeof(TOKEN_OWNER), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ), /* TokenPrimaryGroup */ ICI_SQ_SAME( sizeof(TOKEN_PRIMARY_GROUP), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ), /* TokenDefaultDacl */ ICI_SQ_SAME( sizeof(TOKEN_DEFAULT_DACL), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ), /* TokenSource */ ICI_SQ_SAME( sizeof(TOKEN_SOURCE), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ), /* TokenType */ ICI_SQ_SAME( sizeof(TOKEN_TYPE), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ), /* TokenImpersonationLevel */ ICI_SQ_SAME( sizeof(SECURITY_IMPERSONATION_LEVEL), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ), /* TokenStatistics */ ICI_SQ_SAME( sizeof(TOKEN_STATISTICS), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET | ICIF_SET_SIZE_VARIABLE ), /* TokenRestrictedSids */ ICI_SQ_SAME( sizeof(TOKEN_GROUPS), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ), /* TokenSessionId */ ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* TokenGroupsAndPrivileges */ ICI_SQ_SAME( sizeof(TOKEN_GROUPS_AND_PRIVILEGES), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ), /* TokenSessionReference */ ICI_SQ_SAME( /* FIXME */0, sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ), /* TokenSandBoxInert */ ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ), /* TokenAuditPolicy */ ICI_SQ_SAME( /* FIXME */0, sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ), /* TokenOrigin */ ICI_SQ_SAME( sizeof(TOKEN_ORIGIN), sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ), }; /* FUNCTIONS *****************************************************************/ static NTSTATUS SepCompareTokens(IN PTOKEN FirstToken, IN PTOKEN SecondToken, OUT PBOOLEAN Equal) { BOOLEAN Restricted, IsEqual = FALSE; ASSERT(FirstToken != SecondToken); /* FIXME: Check if every SID that is present in either token is also present in the other one */ Restricted = SeTokenIsRestricted(FirstToken); if (Restricted == SeTokenIsRestricted(SecondToken)) { if (Restricted) { /* FIXME: Check if every SID that is restricted in either token is also restricted in the other one */ } /* FIXME: Check if every privilege that is present in either token is also present in the other one */ } *Equal = IsEqual; return STATUS_SUCCESS; } VOID NTAPI SepFreeProxyData(PVOID ProxyData) { UNIMPLEMENTED; } NTSTATUS NTAPI SepCopyProxyData(PVOID* Dest, PVOID Src) { UNIMPLEMENTED; return(STATUS_NOT_IMPLEMENTED); } NTSTATUS NTAPI SeExchangePrimaryToken(PEPROCESS Process, PACCESS_TOKEN NewTokenP, PACCESS_TOKEN* OldTokenP) { PTOKEN OldToken; PTOKEN NewToken = (PTOKEN)NewTokenP; PAGED_CODE(); if (NewToken->TokenType != TokenPrimary) return(STATUS_BAD_TOKEN_TYPE); if (NewToken->TokenInUse) return(STATUS_TOKEN_ALREADY_IN_USE); /* Mark new token in use */ NewToken->TokenInUse = 1; /* Reference the New Token */ ObReferenceObject(NewToken); /* Replace the old with the new */ OldToken = ObFastReplaceObject(&Process->Token, NewToken); /* Mark the Old Token as free */ OldToken->TokenInUse = 0; *OldTokenP = (PACCESS_TOKEN)OldToken; return STATUS_SUCCESS; } VOID NTAPI SeDeassignPrimaryToken(PEPROCESS Process) { PTOKEN OldToken; /* Remove the Token */ OldToken = ObFastReplaceObject(&Process->Token, NULL); /* Mark the Old Token as free */ OldToken->TokenInUse = 0; } static ULONG RtlLengthSidAndAttributes(ULONG Count, PSID_AND_ATTRIBUTES Src) { ULONG i; ULONG uLength; PAGED_CODE(); uLength = Count * sizeof(SID_AND_ATTRIBUTES); for (i = 0; i < Count; i++) uLength += RtlLengthSid(Src[i].Sid); return(uLength); } NTSTATUS NTAPI SepFindPrimaryGroupAndDefaultOwner(PTOKEN Token, PSID PrimaryGroup, PSID DefaultOwner) { ULONG i; Token->PrimaryGroup = 0; if (DefaultOwner) { Token->DefaultOwnerIndex = Token->UserAndGroupCount; } /* Validate and set the primary group and user pointers */ for (i = 0; i < Token->UserAndGroupCount; i++) { if (DefaultOwner && RtlEqualSid(Token->UserAndGroups[i].Sid, DefaultOwner)) { Token->DefaultOwnerIndex = i; } if (RtlEqualSid(Token->UserAndGroups[i].Sid, PrimaryGroup)) { Token->PrimaryGroup = Token->UserAndGroups[i].Sid; } } if (Token->DefaultOwnerIndex == Token->UserAndGroupCount) { return(STATUS_INVALID_OWNER); } if (Token->PrimaryGroup == 0) { return(STATUS_INVALID_PRIMARY_GROUP); } return(STATUS_SUCCESS); } NTSTATUS NTAPI SepDuplicateToken(PTOKEN Token, POBJECT_ATTRIBUTES ObjectAttributes, BOOLEAN EffectiveOnly, TOKEN_TYPE TokenType, SECURITY_IMPERSONATION_LEVEL Level, KPROCESSOR_MODE PreviousMode, PTOKEN* NewAccessToken) { ULONG uLength; ULONG i; PVOID EndMem; PTOKEN AccessToken; NTSTATUS Status; PAGED_CODE(); Status = ObCreateObject(PreviousMode, SepTokenObjectType, ObjectAttributes, PreviousMode, NULL, sizeof(TOKEN), 0, 0, (PVOID*)&AccessToken); if (!NT_SUCCESS(Status)) { DPRINT1("ObCreateObject() failed (Status %lx)\n", Status); return(Status); } /* Zero out the buffer */ RtlZeroMemory(AccessToken, sizeof(TOKEN)); Status = ZwAllocateLocallyUniqueId(&AccessToken->TokenId); if (!NT_SUCCESS(Status)) { ObDereferenceObject(AccessToken); return(Status); } Status = ZwAllocateLocallyUniqueId(&AccessToken->ModifiedId); if (!NT_SUCCESS(Status)) { ObDereferenceObject(AccessToken); return(Status); } AccessToken->TokenLock = &SepTokenLock; AccessToken->TokenType = TokenType; AccessToken->ImpersonationLevel = Level; RtlCopyLuid(&AccessToken->AuthenticationId, &Token->AuthenticationId); AccessToken->TokenSource.SourceIdentifier.LowPart = Token->TokenSource.SourceIdentifier.LowPart; AccessToken->TokenSource.SourceIdentifier.HighPart = Token->TokenSource.SourceIdentifier.HighPart; memcpy(AccessToken->TokenSource.SourceName, Token->TokenSource.SourceName, sizeof(Token->TokenSource.SourceName)); AccessToken->ExpirationTime.QuadPart = Token->ExpirationTime.QuadPart; AccessToken->UserAndGroupCount = Token->UserAndGroupCount; AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex; uLength = sizeof(SID_AND_ATTRIBUTES) * AccessToken->UserAndGroupCount; for (i = 0; i < Token->UserAndGroupCount; i++) uLength += RtlLengthSid(Token->UserAndGroups[i].Sid); AccessToken->UserAndGroups = (PSID_AND_ATTRIBUTES)ExAllocatePoolWithTag(PagedPool, uLength, 'uKOT'); EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount]; Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount, Token->UserAndGroups, uLength, AccessToken->UserAndGroups, EndMem, &EndMem, &uLength); if (NT_SUCCESS(Status)) { Status = SepFindPrimaryGroupAndDefaultOwner( AccessToken, Token->PrimaryGroup, 0); } if (NT_SUCCESS(Status)) { AccessToken->PrivilegeCount = Token->PrivilegeCount; uLength = AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES); AccessToken->Privileges = (PLUID_AND_ATTRIBUTES)ExAllocatePoolWithTag(PagedPool, uLength, 'pKOT'); for (i = 0; i < AccessToken->PrivilegeCount; i++) { RtlCopyLuid(&AccessToken->Privileges[i].Luid, &Token->Privileges[i].Luid); AccessToken->Privileges[i].Attributes = Token->Privileges[i].Attributes; } if ( Token->DefaultDacl ) { AccessToken->DefaultDacl = (PACL) ExAllocatePoolWithTag(PagedPool, Token->DefaultDacl->AclSize, 'kDOT'); memcpy(AccessToken->DefaultDacl, Token->DefaultDacl, Token->DefaultDacl->AclSize); } } if ( NT_SUCCESS(Status) ) { *NewAccessToken = AccessToken; return(STATUS_SUCCESS); } return(Status); } NTSTATUS NTAPI SeSubProcessToken(IN PTOKEN ParentToken, OUT PTOKEN *Token, IN BOOLEAN InUse, IN ULONG SessionId) { PTOKEN NewToken; OBJECT_ATTRIBUTES ObjectAttributes; NTSTATUS Status; /* Initialize the attributes and duplicate it */ InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); Status = SepDuplicateToken(ParentToken, &ObjectAttributes, FALSE, TokenPrimary, ParentToken->ImpersonationLevel, KernelMode, &NewToken); if (NT_SUCCESS(Status)) { /* Insert it */ Status = ObInsertObject(NewToken, NULL, 0, 0, NULL, NULL); if (NT_SUCCESS(Status)) { /* Set the session ID */ NewToken->SessionId = SessionId; NewToken->TokenInUse = InUse; /* Return the token */ *Token = NewToken; } } /* Return status */ return Status; } NTSTATUS NTAPI SeIsTokenChild(IN PTOKEN Token, OUT PBOOLEAN IsChild) { PTOKEN ProcessToken; LUID ProcessLuid, CallerLuid; /* Assume failure */ *IsChild = FALSE; /* Reference the process token */ ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess()); /* Get the ID */ ProcessLuid = ProcessToken->TokenId; /* Dereference the token */ ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken); /* Get our LUID */ CallerLuid = Token->TokenId; /* Compare the LUIDs */ if (RtlEqualLuid(&CallerLuid, &ProcessLuid)) *IsChild = TRUE; /* Return success */ return STATUS_SUCCESS; } NTSTATUS NTAPI SeCopyClientToken(IN PACCESS_TOKEN Token, IN SECURITY_IMPERSONATION_LEVEL Level, IN KPROCESSOR_MODE PreviousMode, OUT PACCESS_TOKEN* NewToken) { NTSTATUS Status; OBJECT_ATTRIBUTES ObjectAttributes; PAGED_CODE(); InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); Status = SepDuplicateToken(Token, &ObjectAttributes, FALSE, TokenImpersonation, Level, PreviousMode, (PTOKEN*)NewToken); return(Status); } VOID NTAPI SepDeleteToken(PVOID ObjectBody) { PTOKEN AccessToken = (PTOKEN)ObjectBody; if (AccessToken->UserAndGroups) ExFreePool(AccessToken->UserAndGroups); if (AccessToken->Privileges) ExFreePool(AccessToken->Privileges); if (AccessToken->DefaultDacl) ExFreePool(AccessToken->DefaultDacl); } VOID INIT_FUNCTION NTAPI SepInitializeTokenImplementation(VOID) { UNICODE_STRING Name; OBJECT_TYPE_INITIALIZER ObjectTypeInitializer; ExInitializeResource(&SepTokenLock); DPRINT("Creating Token Object Type\n"); /* Initialize the Token type */ RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer)); RtlInitUnicodeString(&Name, L"Token"); ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer); ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK; ObjectTypeInitializer.SecurityRequired = TRUE; ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(TOKEN); ObjectTypeInitializer.GenericMapping = SepTokenMapping; ObjectTypeInitializer.PoolType = PagedPool; ObjectTypeInitializer.ValidAccessMask = TOKEN_ALL_ACCESS; ObjectTypeInitializer.UseDefaultObject = TRUE; ObjectTypeInitializer.DeleteProcedure = SepDeleteToken; ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &SepTokenObjectType); } VOID NTAPI SeAssignPrimaryToken(IN PEPROCESS Process, IN PTOKEN Token) { PAGED_CODE(); /* Sanity checks */ ASSERT(Token->TokenType == TokenPrimary); ASSERT(!Token->TokenInUse); /* Clean any previous token */ if (Process->Token.Object) SeDeassignPrimaryToken(Process); /* Set the new token */ ObReferenceObject(Token); Token->TokenInUse = TRUE; ObInitializeFastReference(&Process->Token, Token); } NTSTATUS NTAPI SepCreateToken(OUT PHANDLE TokenHandle, IN KPROCESSOR_MODE PreviousMode, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN TOKEN_TYPE TokenType, IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, IN PLUID AuthenticationId, IN PLARGE_INTEGER ExpirationTime, IN PSID_AND_ATTRIBUTES User, IN ULONG GroupCount, IN PSID_AND_ATTRIBUTES Groups, IN ULONG GroupLength, IN ULONG PrivilegeCount, IN PLUID_AND_ATTRIBUTES Privileges, IN PSID Owner, IN PSID PrimaryGroup, IN PACL DefaultDacl, IN PTOKEN_SOURCE TokenSource, IN BOOLEAN SystemToken) { PTOKEN AccessToken; LUID TokenId; LUID ModifiedId; PVOID EndMem; ULONG uLength; ULONG i; NTSTATUS Status; ULONG TokenFlags = 0; /* Loop all groups */ for (i = 0; i < GroupCount; i++) { /* Check for mandatory groups */ if (Groups[i].Attributes & SE_GROUP_MANDATORY) { /* Force them to be enabled */ Groups[i].Attributes |= (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT); } /* Check of the group is an admin group */ if (RtlEqualSid(SeAliasAdminsSid, Groups[i].Sid)) { /* Remember this so we can optimize queries later */ TokenFlags |= TOKEN_HAS_ADMIN_GROUP; } } /* Loop all privileges */ for (i = 0; i < PrivilegeCount; i++) { /* For optimization, check for change notify and impersonate rights */ if (((RtlEqualLuid(&Privileges[i].Luid, &SeChangeNotifyPrivilege)) && (Privileges[i].Attributes & SE_PRIVILEGE_ENABLED))) { /* Remember token has traverse */ TokenFlags |= TOKEN_HAS_TRAVERSE_PRIVILEGE; } } Status = ZwAllocateLocallyUniqueId(&TokenId); if (!NT_SUCCESS(Status)) return(Status); Status = ZwAllocateLocallyUniqueId(&ModifiedId); if (!NT_SUCCESS(Status)) return(Status); Status = ObCreateObject(PreviousMode, SepTokenObjectType, ObjectAttributes, PreviousMode, NULL, sizeof(TOKEN), 0, 0, (PVOID*)&AccessToken); if (!NT_SUCCESS(Status)) { DPRINT1("ObCreateObject() failed (Status %lx)\n"); return(Status); } /* Zero out the buffer */ RtlZeroMemory(AccessToken, sizeof(TOKEN)); AccessToken->TokenLock = &SepTokenLock; RtlCopyLuid(&AccessToken->TokenSource.SourceIdentifier, &TokenSource->SourceIdentifier); memcpy(AccessToken->TokenSource.SourceName, TokenSource->SourceName, sizeof(TokenSource->SourceName)); RtlCopyLuid(&AccessToken->TokenId, &TokenId); RtlCopyLuid(&AccessToken->AuthenticationId, AuthenticationId); AccessToken->ExpirationTime = *ExpirationTime; RtlCopyLuid(&AccessToken->ModifiedId, &ModifiedId); AccessToken->UserAndGroupCount = GroupCount + 1; AccessToken->PrivilegeCount = PrivilegeCount; AccessToken->TokenFlags = TokenFlags; AccessToken->TokenType = TokenType; AccessToken->ImpersonationLevel = ImpersonationLevel; /* * Normally we would just point these members into the variable information * area; however, our ObCreateObject() call can't allocate a variable information * area, so we allocate them seperately and provide a destroy function. */ uLength = sizeof(SID_AND_ATTRIBUTES) * AccessToken->UserAndGroupCount; uLength += RtlLengthSid(User); for (i = 0; i < GroupCount; i++) uLength += RtlLengthSid(Groups[i].Sid); AccessToken->UserAndGroups = (PSID_AND_ATTRIBUTES)ExAllocatePoolWithTag(PagedPool, uLength, 'uKOT'); EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount]; Status = RtlCopySidAndAttributesArray(1, User, uLength, AccessToken->UserAndGroups, EndMem, &EndMem, &uLength); if (NT_SUCCESS(Status)) { Status = RtlCopySidAndAttributesArray(GroupCount, Groups, uLength, &AccessToken->UserAndGroups[1], EndMem, &EndMem, &uLength); } if (NT_SUCCESS(Status)) { Status = SepFindPrimaryGroupAndDefaultOwner( AccessToken, PrimaryGroup, Owner); } if (NT_SUCCESS(Status)) { uLength = PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES); AccessToken->Privileges = (PLUID_AND_ATTRIBUTES)ExAllocatePoolWithTag(PagedPool, uLength, 'pKOT'); if (PreviousMode != KernelMode) { _SEH2_TRY { RtlCopyMemory(AccessToken->Privileges, Privileges, PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; } else { RtlCopyMemory(AccessToken->Privileges, Privileges, PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); } } if (NT_SUCCESS(Status)) { AccessToken->DefaultDacl = (PACL) ExAllocatePoolWithTag(PagedPool, DefaultDacl->AclSize, 'kDOT'); memcpy(AccessToken->DefaultDacl, DefaultDacl, DefaultDacl->AclSize); } if (!SystemToken) { Status = ObInsertObject ((PVOID)AccessToken, NULL, DesiredAccess, 0, NULL, TokenHandle); if (!NT_SUCCESS(Status)) { DPRINT1("ObInsertObject() failed (Status %lx)\n", Status); } } else { /* Return pointer instead of handle */ *TokenHandle = (HANDLE)AccessToken; } return Status; } PTOKEN NTAPI SepCreateSystemProcessToken(VOID) { LUID_AND_ATTRIBUTES Privileges[25]; ULONG GroupAttributes, OwnerAttributes; SID_AND_ATTRIBUTES Groups[32]; LARGE_INTEGER Expiration; SID_AND_ATTRIBUTES UserSid; ULONG GroupLength; PSID PrimaryGroup; OBJECT_ATTRIBUTES ObjectAttributes; PSID Owner; ULONG i; PTOKEN Token; NTSTATUS Status; /* Don't ever expire */ Expiration.QuadPart = -1; /* All groups mandatory and enabled */ GroupAttributes = SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT; OwnerAttributes = SE_GROUP_ENABLED | SE_GROUP_OWNER | SE_GROUP_ENABLED_BY_DEFAULT; /* User is system */ UserSid.Sid = SeLocalSystemSid; UserSid.Attributes = 0; /* Primary group is local system */ PrimaryGroup = SeLocalSystemSid; /* Owner is admins */ Owner = SeAliasAdminsSid; /* Groups are admins, world, and authenticated users */ Groups[0].Sid = SeAliasAdminsSid; Groups[0].Attributes = OwnerAttributes; Groups[1].Sid = SeWorldSid; Groups[1].Attributes = GroupAttributes; Groups[2].Sid = SeAuthenticatedUserSid; Groups[2].Attributes = OwnerAttributes; GroupLength = sizeof(SID_AND_ATTRIBUTES) + SeLengthSid(Groups[0].Sid) + SeLengthSid(Groups[1].Sid) + SeLengthSid(Groups[2].Sid); ASSERT(GroupLength <= sizeof(Groups)); /* Setup the privileges */ i = 0; Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED; Privileges[i++].Luid = SeTcbPrivilege; Privileges[i].Attributes = 0; Privileges[i++].Luid = SeCreateTokenPrivilege; Privileges[i].Attributes = 0; Privileges[i++].Luid = SeTakeOwnershipPrivilege; Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT|SE_PRIVILEGE_ENABLED; Privileges[i++].Luid = SeCreatePagefilePrivilege; Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT|SE_PRIVILEGE_ENABLED; Privileges[i++].Luid = SeLockMemoryPrivilege; Privileges[i].Attributes = 0; Privileges[i++].Luid = SeAssignPrimaryTokenPrivilege; Privileges[i].Attributes = 0; Privileges[i++].Luid = SeIncreaseQuotaPrivilege; Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT|SE_PRIVILEGE_ENABLED; Privileges[i++].Luid = SeIncreaseBasePriorityPrivilege; Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT|SE_PRIVILEGE_ENABLED; Privileges[i++].Luid = SeCreatePermanentPrivilege; Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT|SE_PRIVILEGE_ENABLED; Privileges[i++].Luid = SeDebugPrivilege; Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT|SE_PRIVILEGE_ENABLED; Privileges[i++].Luid = SeAuditPrivilege; Privileges[i].Attributes = 0; Privileges[i++].Luid = SeSecurityPrivilege; Privileges[i].Attributes = 0; Privileges[i++].Luid = SeSystemEnvironmentPrivilege; Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT|SE_PRIVILEGE_ENABLED; Privileges[i++].Luid = SeChangeNotifyPrivilege; Privileges[i].Attributes = 0; Privileges[i++].Luid = SeBackupPrivilege; Privileges[i].Attributes = 0; Privileges[i++].Luid = SeRestorePrivilege; Privileges[i].Attributes = 0; Privileges[i++].Luid = SeShutdownPrivilege; Privileges[i].Attributes = 0; Privileges[i++].Luid = SeLoadDriverPrivilege; Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT|SE_PRIVILEGE_ENABLED; Privileges[i++].Luid = SeProfileSingleProcessPrivilege; Privileges[i].Attributes = 0; Privileges[i++].Luid = SeSystemtimePrivilege; ASSERT(i == 20); /* Setup the object attributes */ InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); ASSERT(SeSystemDefaultDacl != NULL); /* Create the token */ Status = SepCreateToken((PHANDLE)&Token, KernelMode, 0, &ObjectAttributes, TokenPrimary, 0, &SeSystemAuthenticationId, &Expiration, &UserSid, 3, Groups, GroupLength, 20, Privileges, Owner, PrimaryGroup, SeSystemDefaultDacl, &SeSystemTokenSource, TRUE); ASSERT(Status == STATUS_SUCCESS); /* Return the token */ return Token; } /* PUBLIC FUNCTIONS ***********************************************************/ /* * @unimplemented */ NTSTATUS NTAPI SeFilterToken(IN PACCESS_TOKEN ExistingToken, IN ULONG Flags, IN PTOKEN_GROUPS SidsToDisable OPTIONAL, IN PTOKEN_PRIVILEGES PrivilegesToDelete OPTIONAL, IN PTOKEN_GROUPS RestrictedSids OPTIONAL, OUT PACCESS_TOKEN * FilteredToken) { UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; } /* * @unimplemented */ NTSTATUS NTAPI SeQueryInformationToken(IN PACCESS_TOKEN Token, IN TOKEN_INFORMATION_CLASS TokenInformationClass, OUT PVOID *TokenInformation) { UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; } /* * @implemented */ NTSTATUS NTAPI SeQuerySessionIdToken(IN PACCESS_TOKEN Token, IN PULONG pSessionId) { *pSessionId = ((PTOKEN)Token)->SessionId; return STATUS_SUCCESS; } /* * @implemented */ NTSTATUS NTAPI SeQueryAuthenticationIdToken(IN PACCESS_TOKEN Token, OUT PLUID LogonId) { PAGED_CODE(); *LogonId = ((PTOKEN)Token)->AuthenticationId; return STATUS_SUCCESS; } /* * @implemented */ SECURITY_IMPERSONATION_LEVEL NTAPI SeTokenImpersonationLevel(IN PACCESS_TOKEN Token) { PAGED_CODE(); return ((PTOKEN)Token)->ImpersonationLevel; } /* * @implemented */ TOKEN_TYPE NTAPI SeTokenType(IN PACCESS_TOKEN Token) { PAGED_CODE(); return ((PTOKEN)Token)->TokenType; } /* * @implemented */ BOOLEAN NTAPI SeTokenIsAdmin(IN PACCESS_TOKEN Token) { PAGED_CODE(); return (((PTOKEN)Token)->TokenFlags & TOKEN_WRITE_RESTRICTED) != 0; } /* * @implemented */ BOOLEAN NTAPI SeTokenIsRestricted(IN PACCESS_TOKEN Token) { PAGED_CODE(); return (((PTOKEN)Token)->TokenFlags & TOKEN_IS_RESTRICTED) != 0; } /* * @implemented */ BOOLEAN NTAPI SeTokenIsWriteRestricted(IN PACCESS_TOKEN Token) { PAGED_CODE(); return (((PTOKEN)Token)->TokenFlags & TOKEN_HAS_RESTORE_PRIVILEGE) != 0; } /* SYSTEM CALLS ***************************************************************/ /* * @implemented */ NTSTATUS NTAPI NtQueryInformationToken(IN HANDLE TokenHandle, IN TOKEN_INFORMATION_CLASS TokenInformationClass, OUT PVOID TokenInformation, IN ULONG TokenInformationLength, OUT PULONG ReturnLength) { union { PVOID Ptr; ULONG Ulong; } Unused; PTOKEN Token; ULONG RequiredLength; KPROCESSOR_MODE PreviousMode; NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); PreviousMode = ExGetPreviousMode(); /* Check buffers and class validity */ Status = DefaultQueryInfoBufferCheck(TokenInformationClass, SeTokenInformationClass, sizeof(SeTokenInformationClass) / sizeof(SeTokenInformationClass[0]), TokenInformation, TokenInformationLength, ReturnLength, NULL, PreviousMode); if(!NT_SUCCESS(Status)) { DPRINT("NtQueryInformationToken() failed, Status: 0x%x\n", Status); return Status; } Status = ObReferenceObjectByHandle(TokenHandle, (TokenInformationClass == TokenSource) ? TOKEN_QUERY_SOURCE : TOKEN_QUERY, SepTokenObjectType, PreviousMode, (PVOID*)&Token, NULL); if (NT_SUCCESS(Status)) { switch (TokenInformationClass) { case TokenUser: { PTOKEN_USER tu = (PTOKEN_USER)TokenInformation; DPRINT("NtQueryInformationToken(TokenUser)\n"); RequiredLength = sizeof(TOKEN_USER) + RtlLengthSid(Token->UserAndGroups[0].Sid); _SEH2_TRY { if(TokenInformationLength >= RequiredLength) { Status = RtlCopySidAndAttributesArray(1, &Token->UserAndGroups[0], RequiredLength - sizeof(TOKEN_USER), &tu->User, (PSID)(tu + 1), &Unused.Ptr, &Unused.Ulong); } else { Status = STATUS_BUFFER_TOO_SMALL; } if(ReturnLength != NULL) { *ReturnLength = RequiredLength; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; break; } case TokenGroups: { PTOKEN_GROUPS tg = (PTOKEN_GROUPS)TokenInformation; DPRINT("NtQueryInformationToken(TokenGroups)\n"); RequiredLength = sizeof(tg->GroupCount) + RtlLengthSidAndAttributes(Token->UserAndGroupCount - 1, &Token->UserAndGroups[1]); _SEH2_TRY { if(TokenInformationLength >= RequiredLength) { ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) - ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES)); PSID_AND_ATTRIBUTES Sid = (PSID_AND_ATTRIBUTES)((ULONG_PTR)TokenInformation + sizeof(tg->GroupCount) + ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES))); tg->GroupCount = Token->UserAndGroupCount - 1; Status = RtlCopySidAndAttributesArray(Token->UserAndGroupCount - 1, &Token->UserAndGroups[1], SidLen, &tg->Groups[0], (PSID)Sid, &Unused.Ptr, &Unused.Ulong); } else { Status = STATUS_BUFFER_TOO_SMALL; } if(ReturnLength != NULL) { *ReturnLength = RequiredLength; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; break; } case TokenPrivileges: { PTOKEN_PRIVILEGES tp = (PTOKEN_PRIVILEGES)TokenInformation; DPRINT("NtQueryInformationToken(TokenPrivileges)\n"); RequiredLength = sizeof(tp->PrivilegeCount) + (Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES)); _SEH2_TRY { if(TokenInformationLength >= RequiredLength) { tp->PrivilegeCount = Token->PrivilegeCount; RtlCopyLuidAndAttributesArray(Token->PrivilegeCount, Token->Privileges, &tp->Privileges[0]); } else { Status = STATUS_BUFFER_TOO_SMALL; } if(ReturnLength != NULL) { *ReturnLength = RequiredLength; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; break; } case TokenOwner: { ULONG SidLen; PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation; DPRINT("NtQueryInformationToken(TokenOwner)\n"); SidLen = RtlLengthSid(Token->UserAndGroups[Token->DefaultOwnerIndex].Sid); RequiredLength = sizeof(TOKEN_OWNER) + SidLen; _SEH2_TRY { if(TokenInformationLength >= RequiredLength) { to->Owner = (PSID)(to + 1); Status = RtlCopySid(SidLen, to->Owner, Token->UserAndGroups[Token->DefaultOwnerIndex].Sid); } else { Status = STATUS_BUFFER_TOO_SMALL; } if(ReturnLength != NULL) { *ReturnLength = RequiredLength; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; break; } case TokenPrimaryGroup: { ULONG SidLen; PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation; DPRINT("NtQueryInformationToken(TokenPrimaryGroup)\n"); SidLen = RtlLengthSid(Token->PrimaryGroup); RequiredLength = sizeof(TOKEN_PRIMARY_GROUP) + SidLen; _SEH2_TRY { if(TokenInformationLength >= RequiredLength) { tpg->PrimaryGroup = (PSID)(tpg + 1); Status = RtlCopySid(SidLen, tpg->PrimaryGroup, Token->PrimaryGroup); } else { Status = STATUS_BUFFER_TOO_SMALL; } if(ReturnLength != NULL) { *ReturnLength = RequiredLength; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; break; } case TokenDefaultDacl: { PTOKEN_DEFAULT_DACL tdd = (PTOKEN_DEFAULT_DACL)TokenInformation; DPRINT("NtQueryInformationToken(TokenDefaultDacl)\n"); RequiredLength = sizeof(TOKEN_DEFAULT_DACL); if(Token->DefaultDacl != NULL) { RequiredLength += Token->DefaultDacl->AclSize; } _SEH2_TRY { if(TokenInformationLength >= RequiredLength) { if(Token->DefaultDacl != NULL) { tdd->DefaultDacl = (PACL)(tdd + 1); RtlCopyMemory(tdd->DefaultDacl, Token->DefaultDacl, Token->DefaultDacl->AclSize); } else { tdd->DefaultDacl = NULL; } } else { Status = STATUS_BUFFER_TOO_SMALL; } if(ReturnLength != NULL) { *ReturnLength = RequiredLength; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; break; } case TokenSource: { PTOKEN_SOURCE ts = (PTOKEN_SOURCE)TokenInformation; DPRINT("NtQueryInformationToken(TokenSource)\n"); RequiredLength = sizeof(TOKEN_SOURCE); _SEH2_TRY { if(TokenInformationLength >= RequiredLength) { *ts = Token->TokenSource; } else { Status = STATUS_BUFFER_TOO_SMALL; } if(ReturnLength != NULL) { *ReturnLength = RequiredLength; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; break; } case TokenType: { PTOKEN_TYPE tt = (PTOKEN_TYPE)TokenInformation; DPRINT("NtQueryInformationToken(TokenType)\n"); RequiredLength = sizeof(TOKEN_TYPE); _SEH2_TRY { if(TokenInformationLength >= RequiredLength) { *tt = Token->TokenType; } else { Status = STATUS_BUFFER_TOO_SMALL; } if(ReturnLength != NULL) { *ReturnLength = RequiredLength; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; break; } case TokenImpersonationLevel: { PSECURITY_IMPERSONATION_LEVEL sil = (PSECURITY_IMPERSONATION_LEVEL)TokenInformation; DPRINT("NtQueryInformationToken(TokenImpersonationLevel)\n"); /* Fail if the token is not an impersonation token */ if (Token->TokenType != TokenImpersonation) { Status = STATUS_INVALID_INFO_CLASS; break; } RequiredLength = sizeof(SECURITY_IMPERSONATION_LEVEL); _SEH2_TRY { if(TokenInformationLength >= RequiredLength) { *sil = Token->ImpersonationLevel; } else { Status = STATUS_BUFFER_TOO_SMALL; } if(ReturnLength != NULL) { *ReturnLength = RequiredLength; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; break; } case TokenStatistics: { PTOKEN_STATISTICS ts = (PTOKEN_STATISTICS)TokenInformation; DPRINT("NtQueryInformationToken(TokenStatistics)\n"); RequiredLength = sizeof(TOKEN_STATISTICS); _SEH2_TRY { if(TokenInformationLength >= RequiredLength) { ts->TokenId = Token->TokenId; ts->AuthenticationId = Token->AuthenticationId; ts->ExpirationTime = Token->ExpirationTime; ts->TokenType = Token->TokenType; ts->ImpersonationLevel = Token->ImpersonationLevel; ts->DynamicCharged = Token->DynamicCharged; ts->DynamicAvailable = Token->DynamicAvailable; ts->GroupCount = Token->UserAndGroupCount - 1; ts->PrivilegeCount = Token->PrivilegeCount; ts->ModifiedId = Token->ModifiedId; } else { Status = STATUS_BUFFER_TOO_SMALL; } if(ReturnLength != NULL) { *ReturnLength = RequiredLength; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; break; } case TokenOrigin: { PTOKEN_ORIGIN to = (PTOKEN_ORIGIN)TokenInformation; DPRINT("NtQueryInformationToken(TokenOrigin)\n"); RequiredLength = sizeof(TOKEN_ORIGIN); _SEH2_TRY { if(TokenInformationLength >= RequiredLength) { RtlCopyLuid(&to->OriginatingLogonSession, &Token->AuthenticationId); } else { Status = STATUS_BUFFER_TOO_SMALL; } if(ReturnLength != NULL) { *ReturnLength = RequiredLength; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; break; } case TokenGroupsAndPrivileges: DPRINT1("NtQueryInformationToken(TokenGroupsAndPrivileges) not implemented\n"); Status = STATUS_NOT_IMPLEMENTED; break; case TokenRestrictedSids: { PTOKEN_GROUPS tg = (PTOKEN_GROUPS)TokenInformation; DPRINT("NtQueryInformationToken(TokenRestrictedSids)\n"); RequiredLength = sizeof(tg->GroupCount) + RtlLengthSidAndAttributes(Token->RestrictedSidCount, Token->RestrictedSids); _SEH2_TRY { if(TokenInformationLength >= RequiredLength) { ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) - (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES)); PSID_AND_ATTRIBUTES Sid = (PSID_AND_ATTRIBUTES)((ULONG_PTR)TokenInformation + sizeof(tg->GroupCount) + (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES))); tg->GroupCount = Token->RestrictedSidCount; Status = RtlCopySidAndAttributesArray(Token->RestrictedSidCount, Token->RestrictedSids, SidLen, &tg->Groups[0], (PSID)Sid, &Unused.Ptr, &Unused.Ulong); } else { Status = STATUS_BUFFER_TOO_SMALL; } if(ReturnLength != NULL) { *ReturnLength = RequiredLength; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; break; } case TokenSandBoxInert: DPRINT1("NtQueryInformationToken(TokenSandboxInert) not implemented\n"); Status = STATUS_NOT_IMPLEMENTED; break; case TokenSessionId: { ULONG SessionId = 0; DPRINT("NtQueryInformationToken(TokenSessionId)\n"); Status = SeQuerySessionIdToken(Token, &SessionId); if(NT_SUCCESS(Status)) { _SEH2_TRY { /* buffer size was already verified, no need to check here again */ *(PULONG)TokenInformation = SessionId; if(ReturnLength != NULL) { *ReturnLength = sizeof(ULONG); } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; } break; } default: DPRINT1("NtQueryInformationToken(%d) invalid information class\n", TokenInformationClass); Status = STATUS_INVALID_INFO_CLASS; break; } ObDereferenceObject(Token); } return(Status); } /* * NtSetTokenInformation: Partly implemented. * Unimplemented: * TokenOrigin, TokenDefaultDacl */ NTSTATUS NTAPI NtSetInformationToken(IN HANDLE TokenHandle, IN TOKEN_INFORMATION_CLASS TokenInformationClass, OUT PVOID TokenInformation, IN ULONG TokenInformationLength) { PTOKEN Token; KPROCESSOR_MODE PreviousMode; ULONG NeededAccess = TOKEN_ADJUST_DEFAULT; NTSTATUS Status; PAGED_CODE(); PreviousMode = ExGetPreviousMode(); Status = DefaultSetInfoBufferCheck(TokenInformationClass, SeTokenInformationClass, sizeof(SeTokenInformationClass) / sizeof(SeTokenInformationClass[0]), TokenInformation, TokenInformationLength, PreviousMode); if(!NT_SUCCESS(Status)) { /* Invalid buffers */ DPRINT("NtSetInformationToken() failed, Status: 0x%x\n", Status); return Status; } if(TokenInformationClass == TokenSessionId) { NeededAccess |= TOKEN_ADJUST_SESSIONID; } Status = ObReferenceObjectByHandle(TokenHandle, NeededAccess, SepTokenObjectType, PreviousMode, (PVOID*)&Token, NULL); if (NT_SUCCESS(Status)) { switch (TokenInformationClass) { case TokenOwner: { if(TokenInformationLength >= sizeof(TOKEN_OWNER)) { PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation; PSID InputSid = NULL, CapturedSid; _SEH2_TRY { InputSid = to->Owner; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; Status = SepCaptureSid(InputSid, PreviousMode, PagedPool, FALSE, &CapturedSid); if(NT_SUCCESS(Status)) { RtlCopySid(RtlLengthSid(CapturedSid), Token->UserAndGroups[Token->DefaultOwnerIndex].Sid, CapturedSid); SepReleaseSid(CapturedSid, PreviousMode, FALSE); } } else { Status = STATUS_INFO_LENGTH_MISMATCH; } break; } case TokenPrimaryGroup: { if(TokenInformationLength >= sizeof(TOKEN_PRIMARY_GROUP)) { PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation; PSID InputSid = NULL, CapturedSid; _SEH2_TRY { InputSid = tpg->PrimaryGroup; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; Status = SepCaptureSid(InputSid, PreviousMode, PagedPool, FALSE, &CapturedSid); if(NT_SUCCESS(Status)) { RtlCopySid(RtlLengthSid(CapturedSid), Token->PrimaryGroup, CapturedSid); SepReleaseSid(CapturedSid, PreviousMode, FALSE); } } else { Status = STATUS_INFO_LENGTH_MISMATCH; } break; } case TokenDefaultDacl: { if(TokenInformationLength >= sizeof(TOKEN_DEFAULT_DACL)) { PTOKEN_DEFAULT_DACL tdd = (PTOKEN_DEFAULT_DACL)TokenInformation; PACL InputAcl = NULL; _SEH2_TRY { InputAcl = tdd->DefaultDacl; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; if(InputAcl != NULL) { PACL CapturedAcl; /* capture and copy the dacl */ Status = SepCaptureAcl(InputAcl, PreviousMode, PagedPool, TRUE, &CapturedAcl); if(NT_SUCCESS(Status)) { /* free the previous dacl if present */ if(Token->DefaultDacl != NULL) { ExFreePool(Token->DefaultDacl); } /* set the new dacl */ Token->DefaultDacl = CapturedAcl; } } else { /* clear and free the default dacl if present */ if(Token->DefaultDacl != NULL) { ExFreePool(Token->DefaultDacl); Token->DefaultDacl = NULL; } } } else { Status = STATUS_INFO_LENGTH_MISMATCH; } break; } case TokenSessionId: { ULONG SessionId = 0; _SEH2_TRY { /* buffer size was already verified, no need to check here again */ SessionId = *(PULONG)TokenInformation; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; if(!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode)) { Status = STATUS_PRIVILEGE_NOT_HELD; break; } Token->SessionId = SessionId; break; } default: { Status = STATUS_NOT_IMPLEMENTED; break; } } ObDereferenceObject(Token); } return(Status); } /* * @implemented * * NOTE: Some sources claim 4th param is ImpersonationLevel, but on W2K * this is certainly NOT true, thou i can't say for sure that EffectiveOnly * is correct either. -Gunnar * This is true. EffectiveOnly overrides SQOS.EffectiveOnly. - IAI */ NTSTATUS NTAPI NtDuplicateToken(IN HANDLE ExistingTokenHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN BOOLEAN EffectiveOnly, IN TOKEN_TYPE TokenType, OUT PHANDLE NewTokenHandle) { KPROCESSOR_MODE PreviousMode; HANDLE hToken; PTOKEN Token; PTOKEN NewToken; PSECURITY_QUALITY_OF_SERVICE CapturedSecurityQualityOfService; BOOLEAN QoSPresent; NTSTATUS Status; PAGED_CODE(); PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { _SEH2_TRY { ProbeForWriteHandle(NewTokenHandle); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } Status = SepCaptureSecurityQualityOfService(ObjectAttributes, PreviousMode, PagedPool, FALSE, &CapturedSecurityQualityOfService, &QoSPresent); if(!NT_SUCCESS(Status)) { DPRINT1("NtDuplicateToken() failed to capture QoS! Status: 0x%x\n", Status); return Status; } Status = ObReferenceObjectByHandle(ExistingTokenHandle, TOKEN_DUPLICATE, SepTokenObjectType, PreviousMode, (PVOID*)&Token, NULL); if (NT_SUCCESS(Status)) { Status = SepDuplicateToken(Token, ObjectAttributes, EffectiveOnly, TokenType, (QoSPresent ? CapturedSecurityQualityOfService->ImpersonationLevel : SecurityAnonymous), PreviousMode, &NewToken); ObDereferenceObject(Token); if (NT_SUCCESS(Status)) { Status = ObInsertObject((PVOID)NewToken, NULL, DesiredAccess, 0, NULL, &hToken); if (NT_SUCCESS(Status)) { _SEH2_TRY { *NewTokenHandle = hToken; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; } } } /* free the captured structure */ SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService, PreviousMode, FALSE); return Status; } NTSTATUS NTAPI NtAdjustGroupsToken(IN HANDLE TokenHandle, IN BOOLEAN ResetToDefault, IN PTOKEN_GROUPS NewState, IN ULONG BufferLength, OUT PTOKEN_GROUPS PreviousState OPTIONAL, OUT PULONG ReturnLength) { UNIMPLEMENTED; return(STATUS_NOT_IMPLEMENTED); } /* * @implemented */ NTSTATUS NTAPI NtAdjustPrivilegesToken (IN HANDLE TokenHandle, IN BOOLEAN DisableAllPrivileges, IN PTOKEN_PRIVILEGES NewState, IN ULONG BufferLength, OUT PTOKEN_PRIVILEGES PreviousState OPTIONAL, OUT PULONG ReturnLength OPTIONAL) { // PLUID_AND_ATTRIBUTES Privileges; KPROCESSOR_MODE PreviousMode; ULONG PrivilegeCount; PTOKEN Token; // ULONG Length; ULONG i; ULONG j; ULONG k; ULONG Count; #if 0 ULONG a; ULONG b; ULONG c; #endif NTSTATUS Status; PAGED_CODE(); DPRINT ("NtAdjustPrivilegesToken() called\n"); // PrivilegeCount = NewState->PrivilegeCount; PreviousMode = KeGetPreviousMode (); // SeCaptureLuidAndAttributesArray(NewState->Privileges, // PrivilegeCount, // PreviousMode, // NULL, // 0, // NonPagedPool, // 1, // &Privileges, // &Length); Status = ObReferenceObjectByHandle (TokenHandle, TOKEN_ADJUST_PRIVILEGES | (PreviousState != NULL ? TOKEN_QUERY : 0), SepTokenObjectType, PreviousMode, (PVOID*)&Token, NULL); if (!NT_SUCCESS(Status)) { DPRINT1 ("Failed to reference token (Status %lx)\n", Status); // SeReleaseLuidAndAttributesArray(Privileges, // PreviousMode, // 0); return Status; } #if 0 SepAdjustPrivileges(Token, 0, PreviousMode, PrivilegeCount, Privileges, PreviousState, &a, &b, &c); #endif PrivilegeCount = (BufferLength - FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges)) / sizeof(LUID_AND_ATTRIBUTES); if (PreviousState != NULL) PreviousState->PrivilegeCount = 0; k = 0; if (DisableAllPrivileges == TRUE) { for (i = 0; i < Token->PrivilegeCount; i++) { if (Token->Privileges[i].Attributes != 0) { DPRINT ("Attributes differ\n"); /* Save current privilege */ if (PreviousState != NULL) { if (k < PrivilegeCount) { PreviousState->PrivilegeCount++; PreviousState->Privileges[k].Luid = Token->Privileges[i].Luid; PreviousState->Privileges[k].Attributes = Token->Privileges[i].Attributes; } else { /* FIXME: Should revert all the changes, calculate how * much space would be needed, set ResultLength * accordingly and fail. */ } k++; } /* Update current privlege */ Token->Privileges[i].Attributes &= ~SE_PRIVILEGE_ENABLED; } } Status = STATUS_SUCCESS; } else { Count = 0; for (i = 0; i < Token->PrivilegeCount; i++) { for (j = 0; j < NewState->PrivilegeCount; j++) { if (Token->Privileges[i].Luid.LowPart == NewState->Privileges[j].Luid.LowPart && Token->Privileges[i].Luid.HighPart == NewState->Privileges[j].Luid.HighPart) { DPRINT ("Found privilege\n"); if ((Token->Privileges[i].Attributes & SE_PRIVILEGE_ENABLED) != (NewState->Privileges[j].Attributes & SE_PRIVILEGE_ENABLED)) { DPRINT ("Attributes differ\n"); DPRINT ("Current attributes %lx desired attributes %lx\n", Token->Privileges[i].Attributes, NewState->Privileges[j].Attributes); /* Save current privilege */ if (PreviousState != NULL) { if (k < PrivilegeCount) { PreviousState->PrivilegeCount++; PreviousState->Privileges[k].Luid = Token->Privileges[i].Luid; PreviousState->Privileges[k].Attributes = Token->Privileges[i].Attributes; } else { /* FIXME: Should revert all the changes, calculate how * much space would be needed, set ResultLength * accordingly and fail. */ } k++; } /* Update current privlege */ Token->Privileges[i].Attributes &= ~SE_PRIVILEGE_ENABLED; Token->Privileges[i].Attributes |= (NewState->Privileges[j].Attributes & SE_PRIVILEGE_ENABLED); DPRINT ("New attributes %lx\n", Token->Privileges[i].Attributes); } Count++; } } } Status = Count < NewState->PrivilegeCount ? STATUS_NOT_ALL_ASSIGNED : STATUS_SUCCESS; } if (ReturnLength != NULL) { *ReturnLength = sizeof(TOKEN_PRIVILEGES) + (sizeof(LUID_AND_ATTRIBUTES) * (k - 1)); } ObDereferenceObject (Token); // SeReleaseLuidAndAttributesArray(Privileges, // PreviousMode, // 0); DPRINT ("NtAdjustPrivilegesToken() done\n"); return Status; } NTSTATUS NTAPI NtCreateToken(OUT PHANDLE TokenHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN TOKEN_TYPE TokenType, IN PLUID AuthenticationId, IN PLARGE_INTEGER ExpirationTime, IN PTOKEN_USER TokenUser, IN PTOKEN_GROUPS TokenGroups, IN PTOKEN_PRIVILEGES TokenPrivileges, IN PTOKEN_OWNER TokenOwner, IN PTOKEN_PRIMARY_GROUP TokenPrimaryGroup, IN PTOKEN_DEFAULT_DACL TokenDefaultDacl, IN PTOKEN_SOURCE TokenSource) { HANDLE hToken; KPROCESSOR_MODE PreviousMode; ULONG nTokenPrivileges = 0; LARGE_INTEGER LocalExpirationTime = {{0, 0}}; NTSTATUS Status; PAGED_CODE(); PreviousMode = ExGetPreviousMode(); if(PreviousMode != KernelMode) { _SEH2_TRY { ProbeForWriteHandle(TokenHandle); ProbeForRead(AuthenticationId, sizeof(LUID), sizeof(ULONG)); LocalExpirationTime = ProbeForReadLargeInteger(ExpirationTime); ProbeForRead(TokenUser, sizeof(TOKEN_USER), sizeof(ULONG)); ProbeForRead(TokenGroups, sizeof(TOKEN_GROUPS), sizeof(ULONG)); ProbeForRead(TokenPrivileges, sizeof(TOKEN_PRIVILEGES), sizeof(ULONG)); ProbeForRead(TokenOwner, sizeof(TOKEN_OWNER), sizeof(ULONG)); ProbeForRead(TokenPrimaryGroup, sizeof(TOKEN_PRIMARY_GROUP), sizeof(ULONG)); ProbeForRead(TokenDefaultDacl, sizeof(TOKEN_DEFAULT_DACL), sizeof(ULONG)); ProbeForRead(TokenSource, sizeof(TOKEN_SOURCE), sizeof(ULONG)); nTokenPrivileges = TokenPrivileges->PrivilegeCount; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } else { nTokenPrivileges = TokenPrivileges->PrivilegeCount; LocalExpirationTime = *ExpirationTime; } Status = SepCreateToken(&hToken, PreviousMode, DesiredAccess, ObjectAttributes, TokenType, ((PSECURITY_QUALITY_OF_SERVICE)(ObjectAttributes->SecurityQualityOfService))->ImpersonationLevel, AuthenticationId, &LocalExpirationTime, &TokenUser->User, TokenGroups->GroupCount, TokenGroups->Groups, 0, // FIXME: Should capture nTokenPrivileges, TokenPrivileges->Privileges, TokenOwner->Owner, TokenPrimaryGroup->PrimaryGroup, TokenDefaultDacl->DefaultDacl, TokenSource, FALSE); if (NT_SUCCESS(Status)) { _SEH2_TRY { *TokenHandle = hToken; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; } return Status; } /* * @implemented */ NTSTATUS NTAPI NtOpenThreadTokenEx(IN HANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN BOOLEAN OpenAsSelf, IN ULONG HandleAttributes, OUT PHANDLE TokenHandle) { PETHREAD Thread, NewThread; HANDLE hToken; PTOKEN Token, NewToken = NULL, PrimaryToken; BOOLEAN CopyOnOpen, EffectiveOnly; SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; SE_IMPERSONATION_STATE ImpersonationState; OBJECT_ATTRIBUTES ObjectAttributes; SECURITY_DESCRIPTOR SecurityDescriptor; PACL Dacl = NULL; KPROCESSOR_MODE PreviousMode; NTSTATUS Status; PAGED_CODE(); PreviousMode = ExGetPreviousMode(); if (PreviousMode != KernelMode) { _SEH2_TRY { ProbeForWriteHandle(TokenHandle); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } /* * At first open the thread token for information access and verify * that the token associated with thread is valid. */ Status = ObReferenceObjectByHandle(ThreadHandle, THREAD_QUERY_INFORMATION, PsThreadType, PreviousMode, (PVOID*)&Thread, NULL); if (!NT_SUCCESS(Status)) { return Status; } Token = PsReferenceImpersonationToken(Thread, &CopyOnOpen, &EffectiveOnly, &ImpersonationLevel); if (Token == NULL) { ObDereferenceObject(Thread); return STATUS_NO_TOKEN; } if (ImpersonationLevel == SecurityAnonymous) { PsDereferenceImpersonationToken(Token); ObDereferenceObject(Thread); return STATUS_CANT_OPEN_ANONYMOUS; } /* * Revert to self if OpenAsSelf is specified. */ if (OpenAsSelf) { PsDisableImpersonation(PsGetCurrentThread(), &ImpersonationState); } if (CopyOnOpen) { Status = ObReferenceObjectByHandle(ThreadHandle, THREAD_ALL_ACCESS, PsThreadType, KernelMode, (PVOID*)&NewThread, NULL); if (NT_SUCCESS(Status)) { PrimaryToken = PsReferencePrimaryToken(NewThread->ThreadsProcess); Status = SepCreateImpersonationTokenDacl(Token, PrimaryToken, &Dacl); ObFastDereferenceObject(&NewThread->ThreadsProcess->Token, PrimaryToken); if (NT_SUCCESS(Status)) { if (Dacl) { RtlCreateSecurityDescriptor(&SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION); RtlSetDaclSecurityDescriptor(&SecurityDescriptor, TRUE, Dacl, FALSE); } InitializeObjectAttributes(&ObjectAttributes, NULL, HandleAttributes, NULL, Dacl ? &SecurityDescriptor : NULL); Status = SepDuplicateToken(Token, &ObjectAttributes, EffectiveOnly, TokenImpersonation, ImpersonationLevel, KernelMode, &NewToken); if (NT_SUCCESS(Status)) { ObReferenceObject(NewToken); Status = ObInsertObject(NewToken, NULL, DesiredAccess, 0, NULL, &hToken); } } } } else { Status = ObOpenObjectByPointer(Token, HandleAttributes, NULL, DesiredAccess, SepTokenObjectType, PreviousMode, &hToken); } if (Dacl) ExFreePool(Dacl); if (OpenAsSelf) { PsRestoreImpersonation(PsGetCurrentThread(), &ImpersonationState); } ObDereferenceObject(Token); if (NT_SUCCESS(Status) && CopyOnOpen) { PsImpersonateClient(Thread, NewToken, FALSE, EffectiveOnly, ImpersonationLevel); } if (NewToken) ObDereferenceObject(NewToken); if (CopyOnOpen && NewThread) ObDereferenceObject(NewThread); if(NT_SUCCESS(Status)) { _SEH2_TRY { *TokenHandle = hToken; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; } return Status; } /* * @implemented */ NTSTATUS NTAPI NtOpenThreadToken(IN HANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN BOOLEAN OpenAsSelf, OUT PHANDLE TokenHandle) { return NtOpenThreadTokenEx(ThreadHandle, DesiredAccess, OpenAsSelf, 0, TokenHandle); } /* * @unimplemented */ NTSTATUS NTAPI NtCompareTokens(IN HANDLE FirstTokenHandle, IN HANDLE SecondTokenHandle, OUT PBOOLEAN Equal) { KPROCESSOR_MODE PreviousMode; PTOKEN FirstToken, SecondToken; BOOLEAN IsEqual; NTSTATUS Status; PAGED_CODE(); PreviousMode = ExGetPreviousMode(); if (PreviousMode != KernelMode) { _SEH2_TRY { ProbeForWriteBoolean(Equal); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } Status = ObReferenceObjectByHandle(FirstTokenHandle, TOKEN_QUERY, SepTokenObjectType, PreviousMode, (PVOID*)&FirstToken, NULL); if (!NT_SUCCESS(Status)) return Status; Status = ObReferenceObjectByHandle(SecondTokenHandle, TOKEN_QUERY, SepTokenObjectType, PreviousMode, (PVOID*)&SecondToken, NULL); if (!NT_SUCCESS(Status)) { ObDereferenceObject(FirstToken); return Status; } if (FirstToken != SecondToken) { Status = SepCompareTokens(FirstToken, SecondToken, &IsEqual); } else IsEqual = TRUE; ObDereferenceObject(FirstToken); ObDereferenceObject(SecondToken); if (NT_SUCCESS(Status)) { _SEH2_TRY { *Equal = IsEqual; } _SEH2_EXCEPT(ExSystemExceptionFilter()) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; } return Status; } NTSTATUS NTAPI NtFilterToken(IN HANDLE ExistingTokenHandle, IN ULONG Flags, IN PTOKEN_GROUPS SidsToDisable OPTIONAL, IN PTOKEN_PRIVILEGES PrivilegesToDelete OPTIONAL, IN PTOKEN_GROUPS RestrictedSids OPTIONAL, OUT PHANDLE NewTokenHandle) { UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; } /* * @unimplemented */ NTSTATUS NTAPI NtImpersonateAnonymousToken(IN HANDLE Thread) { UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; } /* EOF */