[NTOS:SE] Overhaul the token management code.
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Tue, 26 Jun 2018 20:50:53 +0000 (22:50 +0200)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Wed, 27 Jun 2018 21:40:18 +0000 (23:40 +0200)
- Overhaul SepCreateToken() and SepDuplicateToken() so that they
  implement the "variable information area" of the token, where
  immutable lists of user & groups and privileges reside, and the
  "dynamic information area" (allocated separately in paged pool),
  where mutable data such as the token's default DACL is stored.
  Perform the necessary adaptations in SepDeleteToken() and in
  NtSetInformationToken().

- Actually dereference the token's logon session, when needed, in the
  'TokenSessionReference' case in NtSetInformationToken().

- Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns
  the indices of candidate primary group and default owner within the
  token's user & groups array. This allows for fixing the 'TokenOwner'
  and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the
  owner or primary group being set *MUST* already exist in the token's
  user & groups array (as a by-product, memory corruptions that existed
  before due to the broken way of setting these properties disappear too).

- Lock tokens every time operations are performed on them (NOTE: we
  still use a global token lock!).

- Touch the ModifiedId LUID member of tokens everytime a write operation
  (property change, etc...) is made on them.

- Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken().

- Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken().

- Update the token pool tags.

- Explicitly use the Ex*ResourceLite() versions of the locking functions
  in the token locking macros.

ntoskrnl/include/internal/se.h
ntoskrnl/include/internal/tag.h
ntoskrnl/se/token.c
sdk/include/ndk/sefuncs.h

index e2861aa..993c6ce 100644 (file)
@@ -200,17 +200,17 @@ extern PSECURITY_DESCRIPTOR SeUnrestrictedSd;
 #define SepAcquireTokenLockExclusive(Token)                                    \
 {                                                                              \
     KeEnterCriticalRegion();                                                   \
-    ExAcquireResourceExclusive(((PTOKEN)Token)->TokenLock, TRUE);              \
+    ExAcquireResourceExclusiveLite(((PTOKEN)Token)->TokenLock, TRUE);          \
 }
 #define SepAcquireTokenLockShared(Token)                                       \
 {                                                                              \
     KeEnterCriticalRegion();                                                   \
-    ExAcquireResourceShared(((PTOKEN)Token)->TokenLock, TRUE);                 \
+    ExAcquireResourceSharedLite(((PTOKEN)Token)->TokenLock, TRUE);             \
 }
 
 #define SepReleaseTokenLock(Token)                                             \
 {                                                                              \
-    ExReleaseResource(((PTOKEN)Token)->TokenLock);                             \
+    ExReleaseResourceLite(((PTOKEN)Token)->TokenLock);                         \
     KeLeaveCriticalRegion();                                                   \
 }
 
@@ -345,9 +345,9 @@ SeAuditProcessCreate(IN PEPROCESS Process);
 NTSTATUS
 NTAPI
 SeExchangePrimaryToken(
-    struct _EPROCESS* Process,
-    PACCESS_TOKEN NewToken,
-    PACCESS_TOKEN* OldTokenP
+    _In_ PEPROCESS Process,
+    _In_ PACCESS_TOKEN NewAccessToken,
+    _Out_ PACCESS_TOKEN* OldAccessToken
 );
 
 VOID
index eec8013..cca581f 100644 (file)
 #define TAG_QOS               'sQeS'
 #define TAG_LUID              'uLeS'
 #define TAG_PRIVILEGE_SET     'rPeS'
-#define TAG_TOKEN_USERS       'uKOT'
-#define TAG_TOKEN_PRIVILAGES  'pKOT'
-#define TAG_TOKEN_ACL         'kDOT'
+#define TAG_TOKEN_DYNAMIC     'dTeS'
 
 /* LPC Tags */
 #define TAG_LPC_MESSAGE   'McpL'
index 9523fbf..0b20030 100644 (file)
@@ -32,7 +32,7 @@ typedef struct _TOKEN_AUDIT_POLICY_INFORMATION
 /* GLOBALS ********************************************************************/
 
 POBJECT_TYPE SeTokenObjectType = NULL;
-ERESOURCE SepTokenLock;
+ERESOURCE SepTokenLock; // FIXME: Global lock!
 
 TOKEN_SOURCE SeSystemTokenSource = {"*SYSTEM*", {0}};
 LUID SeSystemAuthenticationId = SYSTEM_LUID;
@@ -97,6 +97,10 @@ SepCompareTokens(IN PTOKEN FirstToken,
 
     ASSERT(FirstToken != SecondToken);
 
+    /* Lock the tokens */
+    SepAcquireTokenLockShared(FirstToken);
+    SepAcquireTokenLockShared(SecondToken);
+
     /* FIXME: Check if every SID that is present in either token is also present in the other one */
 
     Restricted = SeTokenIsRestricted(FirstToken);
@@ -112,6 +116,10 @@ SepCompareTokens(IN PTOKEN FirstToken,
         IsEqual = TRUE;
     }
 
+    /* Unlock the tokens */
+    SepReleaseTokenLock(SecondToken);
+    SepReleaseTokenLock(FirstToken);
+
     *Equal = IsEqual;
     return STATUS_SUCCESS;
 }
@@ -223,16 +231,19 @@ SepCopyProxyData(PVOID* Dest,
 
 NTSTATUS
 NTAPI
-SeExchangePrimaryToken(PEPROCESS Process,
-                       PACCESS_TOKEN NewTokenP,
-                       PACCESS_TOKEN* OldTokenP)
+SeExchangePrimaryToken(
+    _In_ PEPROCESS Process,
+    _In_ PACCESS_TOKEN NewAccessToken,
+    _Out_ PACCESS_TOKEN* OldAccessToken)
 {
     PTOKEN OldToken;
-    PTOKEN NewToken = (PTOKEN)NewTokenP;
+    PTOKEN NewToken = (PTOKEN)NewAccessToken;
 
     PAGED_CODE();
 
-    if (NewToken->TokenType != TokenPrimary) return STATUS_BAD_TOKEN_TYPE;
+    if (NewToken->TokenType != TokenPrimary)
+        return STATUS_BAD_TOKEN_TYPE;
+
     if (NewToken->TokenInUse)
     {
         BOOLEAN IsEqual;
@@ -243,43 +254,57 @@ SeExchangePrimaryToken(PEPROCESS Process,
         if (OldToken == NewToken)
         {
             /* So it's a nop. */
-            *OldTokenP = OldToken;
+            *OldAccessToken = OldToken;
             return STATUS_SUCCESS;
         }
 
         Status = SepCompareTokens(OldToken, NewToken, &IsEqual);
         if (!NT_SUCCESS(Status))
         {
-            *OldTokenP = NULL;
             PsDereferencePrimaryToken(OldToken);
+            *OldAccessToken = NULL;
             return Status;
         }
 
         if (!IsEqual)
         {
-            *OldTokenP = NULL;
             PsDereferencePrimaryToken(OldToken);
+            *OldAccessToken = NULL;
             return STATUS_TOKEN_ALREADY_IN_USE;
         }
         /* Silently return STATUS_SUCCESS but do not set the new token,
          * as it's already in use elsewhere. */
-        *OldTokenP = OldToken;
+        *OldAccessToken = OldToken;
         return STATUS_SUCCESS;
     }
 
+    /* Lock the new token */
+    SepAcquireTokenLockExclusive(NewToken);
+
     /* Mark new token in use */
     NewToken->TokenInUse = TRUE;
 
-    /* Reference the New Token */
+    // TODO: Set a correct SessionId for NewToken
+
+    /* Unlock the new token */
+    SepReleaseTokenLock(NewToken);
+
+    /* Reference the new token */
     ObReferenceObject(NewToken);
 
     /* Replace the old with the new */
     OldToken = ObFastReplaceObject(&Process->Token, NewToken);
 
-    /* Mark the Old Token as free */
+    /* Lock the old token */
+    SepAcquireTokenLockExclusive(OldToken);
+
+    /* Mark the old token as free */
     OldToken->TokenInUse = FALSE;
 
-    *OldTokenP = (PACCESS_TOKEN)OldToken;
+    /* Unlock the old token */
+    SepReleaseTokenLock(OldToken);
+
+    *OldAccessToken = (PACCESS_TOKEN)OldToken;
     return STATUS_SUCCESS;
 }
 
@@ -316,44 +341,95 @@ RtlLengthSidAndAttributes(ULONG Count,
 }
 
 
-NTSTATUS
-NTAPI
-SepFindPrimaryGroupAndDefaultOwner(PTOKEN Token,
-                                   PSID PrimaryGroup,
-                                   PSID DefaultOwner)
+static NTSTATUS
+SepFindPrimaryGroupAndDefaultOwner(
+    _In_ PTOKEN Token,
+    _In_ PSID PrimaryGroup,
+    _In_opt_ PSID DefaultOwner,
+    _Out_opt_ PULONG PrimaryGroupIndex,
+    _Out_opt_ PULONG DefaultOwnerIndex)
 {
     ULONG i;
 
-    Token->PrimaryGroup = NULL;
+    /* We should return at least a search result */
+    if (!PrimaryGroupIndex && !DefaultOwnerIndex)
+        return STATUS_INVALID_PARAMETER;
+
+    if (PrimaryGroupIndex)
+    {
+        /* Initialize with an invalid index */
+        // Token->PrimaryGroup = NULL;
+        *PrimaryGroupIndex = Token->UserAndGroupCount;
+    }
 
-    if (DefaultOwner)
+    if (DefaultOwnerIndex)
     {
-        Token->DefaultOwnerIndex = Token->UserAndGroupCount;
+        if (DefaultOwner)
+        {
+            /* An owner is specified: check whether this is actually the user */
+            if (RtlEqualSid(Token->UserAndGroups[0].Sid, DefaultOwner))
+            {
+                /*
+                 * It's the user (first element in array): set it
+                 * as the owner and stop the search for it.
+                 */
+                *DefaultOwnerIndex = 0;
+                DefaultOwnerIndex = NULL;
+            }
+            else
+            {
+                /* An owner is specified: initialize with an invalid index */
+                *DefaultOwnerIndex = Token->UserAndGroupCount;
+            }
+        }
+        else
+        {
+            /*
+             * No owner specified: set the user (first element in array)
+             * as the owner and stop the search for it.
+             */
+            *DefaultOwnerIndex = 0;
+            DefaultOwnerIndex = NULL;
+        }
     }
 
-    /* Validate and set the primary group and user pointers */
+    /* Validate and set the primary group and default owner indices */
     for (i = 0; i < Token->UserAndGroupCount; i++)
     {
-        if (DefaultOwner &&
-            RtlEqualSid(Token->UserAndGroups[i].Sid, DefaultOwner))
+        /* Stop the search if we have found what we searched for */
+        if (!PrimaryGroupIndex && !DefaultOwnerIndex)
+            break;
+
+        if (DefaultOwnerIndex && DefaultOwner &&
+            RtlEqualSid(Token->UserAndGroups[i].Sid, DefaultOwner) &&
+            (Token->UserAndGroups[i].Attributes & SE_GROUP_OWNER))
         {
-            Token->DefaultOwnerIndex = i;
+            /* Owner is found, stop the search for it */
+            *DefaultOwnerIndex = i;
+            DefaultOwnerIndex = NULL;
         }
 
-        if (RtlEqualSid(Token->UserAndGroups[i].Sid, PrimaryGroup))
+        if (PrimaryGroupIndex &&
+            RtlEqualSid(Token->UserAndGroups[i].Sid, PrimaryGroup))
         {
-            Token->PrimaryGroup = Token->UserAndGroups[i].Sid;
+            /* Primary group is found, stop the search for it */
+            // Token->PrimaryGroup = Token->UserAndGroups[i].Sid;
+            *PrimaryGroupIndex = i;
+            PrimaryGroupIndex = NULL;
         }
     }
 
-    if (Token->DefaultOwnerIndex == Token->UserAndGroupCount)
+    if (DefaultOwnerIndex)
     {
-        return STATUS_INVALID_OWNER;
+        if (*DefaultOwnerIndex == Token->UserAndGroupCount)
+            return STATUS_INVALID_OWNER;
     }
 
-    if (Token->PrimaryGroup == NULL)
+    if (PrimaryGroupIndex)
     {
-        return STATUS_INVALID_PRIMARY_GROUP;
+        if (*PrimaryGroupIndex == Token->UserAndGroupCount)
+        // if (Token->PrimaryGroup == NULL)
+            return STATUS_INVALID_PRIMARY_GROUP;
     }
 
     return STATUS_SUCCESS;
@@ -371,20 +447,24 @@ SepDuplicateToken(
     _In_ KPROCESSOR_MODE PreviousMode,
     _Out_ PTOKEN* NewAccessToken)
 {
-    ULONG uLength;
-    ULONG i;
-    PVOID EndMem;
-    PTOKEN AccessToken;
     NTSTATUS Status;
+    PTOKEN AccessToken;
+    PVOID EndMem;
+    ULONG VariableLength;
+    ULONG TotalSize;
 
     PAGED_CODE();
 
+    /* Compute how much size we need to allocate for the token */
+    VariableLength = Token->VariableLength;
+    TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
+
     Status = ObCreateObject(PreviousMode,
                             SeTokenObjectType,
                             ObjectAttributes,
                             PreviousMode,
                             NULL,
-                            sizeof(TOKEN),
+                            TotalSize,
                             0,
                             0,
                             (PVOID*)&AccessToken);
@@ -394,103 +474,193 @@ SepDuplicateToken(
         return Status;
     }
 
-    /* Zero out the buffer */
-    RtlZeroMemory(AccessToken, sizeof(TOKEN));
+    /* Zero out the buffer and initialize the token */
+    RtlZeroMemory(AccessToken, TotalSize);
 
     ExAllocateLocallyUniqueId(&AccessToken->TokenId);
 
-    AccessToken->TokenLock = &SepTokenLock;
+    AccessToken->TokenType = TokenType;
+    AccessToken->ImpersonationLevel = Level;
 
-    /* Copy and reference the logon session */
-    RtlCopyLuid(&AccessToken->AuthenticationId, &Token->AuthenticationId);
-    SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
+    AccessToken->TokenLock = &SepTokenLock; // FIXME: Global lock!
 
-    AccessToken->TokenType  = TokenType;
-    AccessToken->ImpersonationLevel = Level;
-    RtlCopyLuid(&AccessToken->ModifiedId, &Token->ModifiedId);
+    /* Copy the immutable fields */
+    RtlCopyLuid(&AccessToken->TokenSource.SourceIdentifier,
+                &Token->TokenSource.SourceIdentifier);
+    RtlCopyMemory(AccessToken->TokenSource.SourceName,
+                  Token->TokenSource.SourceName,
+                  sizeof(Token->TokenSource.SourceName));
 
-    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;
+    AccessToken->AuthenticationId = Token->AuthenticationId;
+    AccessToken->ParentTokenId = Token->ParentTokenId;
+    AccessToken->ExpirationTime = Token->ExpirationTime;
+    AccessToken->OriginatingLogonSession = Token->OriginatingLogonSession;
 
-    uLength = sizeof(SID_AND_ATTRIBUTES) * AccessToken->UserAndGroupCount;
-    for (i = 0; i < Token->UserAndGroupCount; i++)
-        uLength += RtlLengthSid(Token->UserAndGroups[i].Sid);
 
-    AccessToken->UserAndGroups = ExAllocatePoolWithTag(PagedPool,
-                                                       uLength,
-                                                       TAG_TOKEN_USERS);
-    if (AccessToken->UserAndGroups == NULL)
+    /* Lock the source token and copy the mutable fields */
+    SepAcquireTokenLockExclusive(Token);
+
+    AccessToken->SessionId = Token->SessionId;
+    RtlCopyLuid(&AccessToken->ModifiedId, &Token->ModifiedId);
+
+    AccessToken->TokenFlags = Token->TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
+
+    /* Copy and reference the logon session */
+    // RtlCopyLuid(&AccessToken->AuthenticationId, &Token->AuthenticationId);
+    Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
+    if (!NT_SUCCESS(Status))
     {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto done;
+        /* No logon session could be found, bail out */
+        DPRINT1("SepRmReferenceLogonSession() failed (Status 0x%lx)\n", Status);
+        /* Set the flag for proper cleanup by the delete procedure */
+        AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED;
+        goto Quit;
     }
 
-    EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
+    /* Assign the data that reside in the TOKEN's variable information area */
+    AccessToken->VariableLength = VariableLength;
+    EndMem = (PVOID)&AccessToken->VariablePart;
 
-    Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount,
-                                          Token->UserAndGroups,
-                                          uLength,
-                                          AccessToken->UserAndGroups,
-                                          EndMem,
-                                          &EndMem,
-                                          &uLength);
-    if (!NT_SUCCESS(Status))
-        goto done;
+    /* Copy the privileges */
+    AccessToken->PrivilegeCount = 0;
+    AccessToken->Privileges = NULL;
+    if (Token->Privileges && (Token->PrivilegeCount > 0))
+    {
+        ULONG PrivilegesLength = Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
+        PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID));
+
+        ASSERT(VariableLength >= PrivilegesLength);
+
+        AccessToken->PrivilegeCount = Token->PrivilegeCount;
+        AccessToken->Privileges = EndMem;
+        EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength);
+        VariableLength -= PrivilegesLength;
+
+        RtlCopyMemory(AccessToken->Privileges,
+                      Token->Privileges,
+                      AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
+    }
+
+    /* Copy the user and groups */
+    AccessToken->UserAndGroupCount = 0;
+    AccessToken->UserAndGroups = NULL;
+    if (Token->UserAndGroups && (Token->UserAndGroupCount > 0))
+    {
+        AccessToken->UserAndGroupCount = Token->UserAndGroupCount;
+        AccessToken->UserAndGroups = EndMem;
+        EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
+        VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups);
+
+        Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount,
+                                              Token->UserAndGroups,
+                                              VariableLength,
+                                              AccessToken->UserAndGroups,
+                                              EndMem,
+                                              &EndMem,
+                                              &VariableLength);
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("RtlCopySidAndAttributesArray(UserAndGroups) failed (Status 0x%lx)\n", Status);
+            goto Quit;
+        }
+    }
+
+#if 1
+    {
+    ULONG PrimaryGroupIndex;
 
+    /* Find the token primary group */
     Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken,
                                                 Token->PrimaryGroup,
-                                                0);
+                                                NULL,
+                                                &PrimaryGroupIndex,
+                                                NULL);
     if (!NT_SUCCESS(Status))
-        goto done;
-
-    AccessToken->PrivilegeCount = Token->PrivilegeCount;
-
-    uLength = AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
-    AccessToken->Privileges = ExAllocatePoolWithTag(PagedPool,
-                                                    uLength,
-                                                    TAG_TOKEN_PRIVILAGES);
-    if (AccessToken->Privileges == NULL)
     {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto done;
+        DPRINT1("SepFindPrimaryGroupAndDefaultOwner failed (Status 0x%lx)\n", Status);
+        goto Quit;
+    }
+    AccessToken->PrimaryGroup = AccessToken->UserAndGroups[PrimaryGroupIndex].Sid;
     }
+#else
+    AccessToken->PrimaryGroup = (PVOID)((ULONG_PTR)AccessToken + (ULONG_PTR)Token->PrimaryGroup - (ULONG_PTR)Token->UserAndGroups);
+#endif
+    AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex;
 
-    for (i = 0; i < AccessToken->PrivilegeCount; i++)
-    {
-        RtlCopyLuid(&AccessToken->Privileges[i].Luid,
-                    &Token->Privileges[i].Luid);
-        AccessToken->Privileges[i].Attributes =
-        Token->Privileges[i].Attributes;
+    /* Copy the restricted SIDs */
+    AccessToken->RestrictedSidCount = 0;
+    AccessToken->RestrictedSids = NULL;
+    if (Token->RestrictedSids && (Token->RestrictedSidCount > 0))
+    {
+        AccessToken->RestrictedSidCount = Token->RestrictedSidCount;
+        AccessToken->RestrictedSids = EndMem;
+        EndMem = &AccessToken->RestrictedSids[AccessToken->RestrictedSidCount];
+        VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->RestrictedSids);
+
+        Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount,
+                                              Token->RestrictedSids,
+                                              VariableLength,
+                                              AccessToken->RestrictedSids,
+                                              EndMem,
+                                              &EndMem,
+                                              &VariableLength);
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("RtlCopySidAndAttributesArray(RestrictedSids) failed (Status 0x%lx)\n", Status);
+            goto Quit;
+        }
     }
 
-    if (Token->DefaultDacl)
+
+    //
+    // FIXME: Implement the "EffectiveOnly" option, that removes all
+    // the disabled parts (privileges and groups) of the token.
+    //
+
+
+    //
+    // NOTE: So far our dynamic area only contains
+    // the default dacl, so this makes the following
+    // code pretty simple. The day where it stores
+    // other data, the code will require adaptations.
+    //
+
+    /* Now allocate the TOKEN's dynamic information area and set the data */
+    AccessToken->DynamicAvailable = 0; // Unused memory in the dynamic area.
+    AccessToken->DynamicPart = NULL;
+    if (Token->DynamicPart && Token->DefaultDacl)
     {
-        AccessToken->DefaultDacl = ExAllocatePoolWithTag(PagedPool,
+        AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
                                                          Token->DefaultDacl->AclSize,
-                                                         TAG_TOKEN_ACL);
-        if (AccessToken->DefaultDacl == NULL)
+                                                         TAG_TOKEN_DYNAMIC);
+        if (AccessToken->DynamicPart == NULL)
         {
             Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto done;
+            goto Quit;
         }
+        EndMem = (PVOID)AccessToken->DynamicPart;
 
-        memcpy(AccessToken->DefaultDacl,
-               Token->DefaultDacl,
-               Token->DefaultDacl->AclSize);
+        AccessToken->DefaultDacl = EndMem;
+
+        RtlCopyMemory(AccessToken->DefaultDacl,
+                      Token->DefaultDacl,
+                      Token->DefaultDacl->AclSize);
     }
 
+    /* Unlock the source token */
+    SepReleaseTokenLock(Token);
+
+    /* Return the token */
     *NewAccessToken = AccessToken;
+    Status = STATUS_SUCCESS;
 
-done:
+Quit:
     if (!NT_SUCCESS(Status))
     {
-        /* Dereference the token, the delete procedure will clean up */
+        /* Unlock the source token */
+        SepReleaseTokenLock(Token);
+
+        /* Dereference the token, the delete procedure will clean it up */
         ObDereferenceObject(AccessToken);
     }
 
@@ -609,16 +779,12 @@ SepDeleteToken(PVOID ObjectBody)
     DPRINT("SepDeleteToken()\n");
 
     /* Dereference the logon session */
-    SepRmDereferenceLogonSession(&AccessToken->AuthenticationId);
-
-    if (AccessToken->UserAndGroups)
-        ExFreePoolWithTag(AccessToken->UserAndGroups, TAG_TOKEN_USERS);
-
-    if (AccessToken->Privileges)
-        ExFreePoolWithTag(AccessToken->Privileges, TAG_TOKEN_PRIVILAGES);
+    if ((AccessToken->TokenFlags & TOKEN_SESSION_NOT_REFERENCED) == 0)
+        SepRmDereferenceLogonSession(&AccessToken->AuthenticationId);
 
-    if (AccessToken->DefaultDacl)
-        ExFreePoolWithTag(AccessToken->DefaultDacl, TAG_TOKEN_ACL);
+    /* Delete the dynamic information area */
+    if (AccessToken->DynamicPart)
+        ExFreePoolWithTag(AccessToken->DynamicPart, TAG_TOKEN_DYNAMIC);
 }
 
 
@@ -630,7 +796,7 @@ SepInitializeTokenImplementation(VOID)
     UNICODE_STRING Name;
     OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
 
-    ExInitializeResource(&SepTokenLock);
+    ExInitializeResource(&SepTokenLock); // FIXME: Global lock!
 
     DPRINT("Creating Token Object Type\n");
 
@@ -692,14 +858,18 @@ SepCreateToken(
     _In_ PTOKEN_SOURCE TokenSource,
     _In_ BOOLEAN SystemToken)
 {
+    NTSTATUS Status;
     PTOKEN AccessToken;
+    ULONG TokenFlags = 0;
+    ULONG PrimaryGroupIndex, DefaultOwnerIndex;
     LUID TokenId;
     LUID ModifiedId;
     PVOID EndMem;
-    ULONG uLength;
+    ULONG PrivilegesLength;
+    ULONG UserGroupsLength;
+    ULONG VariableLength;
+    ULONG TotalSize;
     ULONG i;
-    NTSTATUS Status;
-    ULONG TokenFlags = 0;
 
     PAGED_CODE();
 
@@ -721,15 +891,37 @@ SepCreateToken(
         }
     }
 
+    /* Allocate unique IDs for the token */
     ExAllocateLocallyUniqueId(&TokenId);
     ExAllocateLocallyUniqueId(&ModifiedId);
 
+    /* Compute how much size we need to allocate for the token */
+
+    /* Privileges size */
+    PrivilegesLength = PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
+    PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID));
+
+    /* User and groups size */
+    UserGroupsLength = (1 + GroupCount) * sizeof(SID_AND_ATTRIBUTES);
+    UserGroupsLength += RtlLengthSid(User->Sid);
+    for (i = 0; i < GroupCount; i++)
+    {
+        UserGroupsLength += RtlLengthSid(Groups[i].Sid);
+    }
+    UserGroupsLength = ALIGN_UP_BY(UserGroupsLength, sizeof(PVOID));
+
+    /* Add the additional groups array length */
+    UserGroupsLength += ALIGN_UP_BY(GroupsLength, sizeof(PVOID));
+
+    VariableLength = PrivilegesLength + UserGroupsLength;
+    TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
+
     Status = ObCreateObject(PreviousMode,
                             SeTokenObjectType,
                             ObjectAttributes,
                             PreviousMode,
                             NULL,
-                            sizeof(TOKEN),
+                            TotalSize,
                             0,
                             0,
                             (PVOID*)&AccessToken);
@@ -739,141 +931,144 @@ SepCreateToken(
         return Status;
     }
 
-    /* Zero out the buffer */
-    RtlZeroMemory(AccessToken, sizeof(TOKEN));
+    /* Zero out the buffer and initialize the token */
+    RtlZeroMemory(AccessToken, TotalSize);
 
-    AccessToken->TokenLock = &SepTokenLock;
+    RtlCopyLuid(&AccessToken->TokenId, &TokenId);
+
+    AccessToken->TokenType = TokenType;
+    AccessToken->ImpersonationLevel = ImpersonationLevel;
+
+    AccessToken->TokenLock = &SepTokenLock; // FIXME: Global lock!
 
     RtlCopyLuid(&AccessToken->TokenSource.SourceIdentifier,
                 &TokenSource->SourceIdentifier);
-    memcpy(AccessToken->TokenSource.SourceName,
-           TokenSource->SourceName,
-           sizeof(TokenSource->SourceName));
+    RtlCopyMemory(AccessToken->TokenSource.SourceName,
+                  TokenSource->SourceName,
+                  sizeof(TokenSource->SourceName));
 
-    /* Copy and reference the logon session */
-    RtlCopyLuid(&AccessToken->AuthenticationId, AuthenticationId);
-    SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
-
-    RtlCopyLuid(&AccessToken->TokenId, &TokenId);
     AccessToken->ExpirationTime = *ExpirationTime;
     RtlCopyLuid(&AccessToken->ModifiedId, &ModifiedId);
 
-    AccessToken->UserAndGroupCount = GroupCount + 1;
-    AccessToken->PrivilegeCount = PrivilegeCount;
+    AccessToken->TokenFlags = TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
 
-    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.
-     */
+    /* Copy and reference the logon session */
+    RtlCopyLuid(&AccessToken->AuthenticationId, AuthenticationId);
+    Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
+    if (!NT_SUCCESS(Status))
+    {
+        /* No logon session could be found, bail out */
+        DPRINT1("SepRmReferenceLogonSession() failed (Status 0x%lx)\n", Status);
+        /* Set the flag for proper cleanup by the delete procedure */
+        AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED;
+        goto Quit;
+    }
 
-    uLength = sizeof(SID_AND_ATTRIBUTES) * AccessToken->UserAndGroupCount;
-    uLength += RtlLengthSid(User->Sid);
-    for (i = 0; i < GroupCount; i++)
-        uLength += RtlLengthSid(Groups[i].Sid);
+    /* Assign the data that reside in the TOKEN's variable information area */
+    AccessToken->VariableLength = VariableLength;
+    EndMem = (PVOID)&AccessToken->VariablePart;
 
-    // FIXME: should use the object itself
-    AccessToken->UserAndGroups = ExAllocatePoolWithTag(PagedPool,
-                                                       uLength,
-                                                       TAG_TOKEN_USERS);
-    if (AccessToken->UserAndGroups == NULL)
+    /* Copy the privileges */
+    AccessToken->PrivilegeCount = PrivilegeCount;
+    AccessToken->Privileges = NULL;
+    if (PrivilegeCount > 0)
     {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto done;
+        AccessToken->Privileges = EndMem;
+        EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength);
+        VariableLength -= PrivilegesLength;
+
+        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))
+            goto Quit;
     }
 
+    /* Update the privilege flags */
+    SepUpdatePrivilegeFlagsToken(AccessToken);
+
+    /* Copy the user and groups */
+    AccessToken->UserAndGroupCount = 1 + GroupCount;
+    AccessToken->UserAndGroups = EndMem;
     EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
+    VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups);
 
     Status = RtlCopySidAndAttributesArray(1,
                                           User,
-                                          uLength,
-                                          AccessToken->UserAndGroups,
+                                          VariableLength,
+                                          &AccessToken->UserAndGroups[0],
                                           EndMem,
                                           &EndMem,
-                                          &uLength);
+                                          &VariableLength);
     if (!NT_SUCCESS(Status))
-        goto done;
+        goto Quit;
 
     Status = RtlCopySidAndAttributesArray(GroupCount,
                                           Groups,
-                                          uLength,
+                                          VariableLength,
                                           &AccessToken->UserAndGroups[1],
                                           EndMem,
                                           &EndMem,
-                                          &uLength);
+                                          &VariableLength);
     if (!NT_SUCCESS(Status))
-        goto done;
+        goto Quit;
 
+    /* Find the token primary group and default owner */
     Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken,
                                                 PrimaryGroup,
-                                                Owner);
+                                                Owner,
+                                                &PrimaryGroupIndex,
+                                                &DefaultOwnerIndex);
     if (!NT_SUCCESS(Status))
-        goto done;
-
-    // FIXME: should use the object itself
-    uLength = PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
-    if (uLength == 0) uLength = sizeof(PVOID);
-    AccessToken->Privileges = ExAllocatePoolWithTag(PagedPool,
-                                                    uLength,
-                                                    TAG_TOKEN_PRIVILAGES);
-    if (AccessToken->Privileges == NULL)
-    {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto done;
-    }
-
-    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));
+        DPRINT1("SepFindPrimaryGroupAndDefaultOwner failed (Status 0x%lx)\n", Status);
+        goto Quit;
     }
 
-    if (!NT_SUCCESS(Status))
-        goto done;
-
-    /* Update privilege flags */
-    SepUpdatePrivilegeFlagsToken(AccessToken);
+    AccessToken->PrimaryGroup = AccessToken->UserAndGroups[PrimaryGroupIndex].Sid;
+    AccessToken->DefaultOwnerIndex = DefaultOwnerIndex;
 
+    /* Now allocate the TOKEN's dynamic information area and set the data */
+    AccessToken->DynamicAvailable = 0; // Unused memory in the dynamic area.
+    AccessToken->DynamicPart = NULL;
     if (DefaultDacl != NULL)
     {
-        // FIXME: should use the object itself
-        AccessToken->DefaultDacl = ExAllocatePoolWithTag(PagedPool,
+        AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
                                                          DefaultDacl->AclSize,
-                                                         TAG_TOKEN_ACL);
-        if (AccessToken->DefaultDacl == NULL)
+                                                         TAG_TOKEN_DYNAMIC);
+        if (AccessToken->DynamicPart == NULL)
         {
             Status = STATUS_INSUFFICIENT_RESOURCES;
-            goto done;
+            goto Quit;
         }
+        EndMem = (PVOID)AccessToken->DynamicPart;
+
+        AccessToken->DefaultDacl = EndMem;
 
         RtlCopyMemory(AccessToken->DefaultDacl,
                       DefaultDacl,
                       DefaultDacl->AclSize);
     }
-    else
-    {
-        AccessToken->DefaultDacl = NULL;
-    }
 
+    /* Insert the token only if it's not the system token, otherwise return it directly */
     if (!SystemToken)
     {
         Status = ObInsertObject(AccessToken,
@@ -893,10 +1088,10 @@ SepCreateToken(
         *TokenHandle = (HANDLE)AccessToken;
     }
 
-done:
+Quit:
     if (!NT_SUCCESS(Status))
     {
-        /* Dereference the token, the delete procedure will clean up */
+        /* Dereference the token, the delete procedure will clean it up */
         ObDereferenceObject(AccessToken);
     }
 
@@ -927,23 +1122,23 @@ SepCreateSystemProcessToken(VOID)
     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 */
+    /* User is Local System */
     UserSid.Sid = SeLocalSystemSid;
     UserSid.Attributes = 0;
 
-    /* Primary group is local system */
+    /* Primary group is Local System */
     PrimaryGroup = SeLocalSystemSid;
 
-    /* Owner is admins */
+    /* Owner is Administrators */
     Owner = SeAliasAdminsSid;
 
-    /* Groups are admins, world, and authenticated users */
+    /* Groups are Administrators, 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;
+    Groups[2].Sid = SeAuthenticatedUsersSid;
+    Groups[2].Attributes = GroupAttributes;
     GroupsLength = sizeof(SID_AND_ATTRIBUTES) +
                    SeLengthSid(Groups[0].Sid) +
                    SeLengthSid(Groups[1].Sid) +
@@ -1023,7 +1218,7 @@ SepCreateSystemProcessToken(VOID)
                             0,
                             &ObjectAttributes,
                             TokenPrimary,
-                            0,
+                            SecurityAnonymous,
                             &SeSystemAuthenticationId,
                             &Expiration,
                             &UserSid,
@@ -1090,6 +1285,8 @@ SeQueryInformationToken(IN PACCESS_TOKEN AccessToken,
         return STATUS_INVALID_INFO_CLASS;
     }
 
+    // TODO: Lock the token
+
     switch (TokenInformationClass)
     {
         case TokenUser:
@@ -1471,10 +1668,7 @@ SeQueryInformationToken(IN PACCESS_TOKEN AccessToken,
         case TokenSessionId:
         {
             DPRINT("SeQueryInformationToken(TokenSessionId)\n");
-
             Status = SeQuerySessionIdToken(Token, (PULONG)TokenInformation);
-
-            // Status = STATUS_SUCCESS;
             break;
         }
 
@@ -1495,7 +1689,16 @@ NTAPI
 SeQuerySessionIdToken(IN PACCESS_TOKEN Token,
                       IN PULONG pSessionId)
 {
+    PAGED_CODE();
+
+    /* Lock the token */
+    SepAcquireTokenLockShared(Token);
+
     *pSessionId = ((PTOKEN)Token)->SessionId;
+
+    /* Unlock the token */
+    SepReleaseTokenLock(Token);
+
     return STATUS_SUCCESS;
 }
 
@@ -1587,12 +1790,17 @@ SeTokenIsWriteRestricted(IN PACCESS_TOKEN Token)
 /*
  * @implemented
  */
-NTSTATUS NTAPI
-NtQueryInformationToken(IN HANDLE TokenHandle,
-                        IN TOKEN_INFORMATION_CLASS TokenInformationClass,
-                        OUT PVOID TokenInformation,
-                        IN ULONG TokenInformationLength,
-                        OUT PULONG ReturnLength)
+_Must_inspect_result_
+__kernel_entry
+NTSTATUS
+NTAPI
+NtQueryInformationToken(
+    _In_ HANDLE TokenHandle,
+    _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
+    _Out_writes_bytes_to_opt_(TokenInformationLength, *ReturnLength)
+        PVOID TokenInformation,
+    _In_ ULONG TokenInformationLength,
+    _Out_ PULONG ReturnLength)
 {
     NTSTATUS Status;
     KPROCESSOR_MODE PreviousMode;
@@ -1631,6 +1839,9 @@ NtQueryInformationToken(IN HANDLE TokenHandle,
                                        NULL);
     if (NT_SUCCESS(Status))
     {
+        /* Lock the token */
+        SepAcquireTokenLockShared(Token);
+
         switch (TokenInformationClass)
         {
             case TokenUser:
@@ -2140,6 +2351,8 @@ NtQueryInformationToken(IN HANDLE TokenHandle,
                 break;
         }
 
+        /* Unlock and dereference the token */
+        SepReleaseTokenLock(Token);
         ObDereferenceObject(Token);
     }
 
@@ -2152,16 +2365,20 @@ NtQueryInformationToken(IN HANDLE TokenHandle,
  * Unimplemented:
  *  TokenOrigin, TokenDefaultDacl
  */
-NTSTATUS NTAPI
-NtSetInformationToken(IN HANDLE TokenHandle,
-                      IN TOKEN_INFORMATION_CLASS TokenInformationClass,
-                      IN PVOID TokenInformation,
-                      IN ULONG TokenInformationLength)
+_Must_inspect_result_
+__kernel_entry
+NTSTATUS
+NTAPI
+NtSetInformationToken(
+    _In_ HANDLE TokenHandle,
+    _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
+    _In_reads_bytes_(TokenInformationLength) PVOID TokenInformation,
+    _In_ ULONG TokenInformationLength)
 {
+    NTSTATUS Status;
     PTOKEN Token;
     KPROCESSOR_MODE PreviousMode;
     ULONG NeededAccess = TOKEN_ADJUST_DEFAULT;
-    NTSTATUS Status;
 
     PAGED_CODE();
 
@@ -2201,6 +2418,7 @@ NtSetInformationToken(IN HANDLE TokenHandle,
                 {
                     PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation;
                     PSID InputSid = NULL, CapturedSid;
+                    ULONG DefaultOwnerIndex;
 
                     _SEH2_TRY
                     {
@@ -2220,9 +2438,25 @@ NtSetInformationToken(IN HANDLE TokenHandle,
                                            &CapturedSid);
                     if (NT_SUCCESS(Status))
                     {
-                        RtlCopySid(RtlLengthSid(CapturedSid),
-                                   Token->UserAndGroups[Token->DefaultOwnerIndex].Sid,
-                                   CapturedSid);
+                        /* Lock the token */
+                        SepAcquireTokenLockExclusive(Token);
+
+                        /* Find the owner amongst the existing token user and groups */
+                        Status = SepFindPrimaryGroupAndDefaultOwner(Token,
+                                                                    NULL,
+                                                                    CapturedSid,
+                                                                    NULL,
+                                                                    &DefaultOwnerIndex);
+                        if (NT_SUCCESS(Status))
+                        {
+                            /* Found it */
+                            Token->DefaultOwnerIndex = DefaultOwnerIndex;
+                            ExAllocateLocallyUniqueId(&Token->ModifiedId);
+                        }
+
+                        /* Unlock the token */
+                        SepReleaseTokenLock(Token);
+
                         SepReleaseSid(CapturedSid,
                                       PreviousMode,
                                       FALSE);
@@ -2241,6 +2475,7 @@ NtSetInformationToken(IN HANDLE TokenHandle,
                 {
                     PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation;
                     PSID InputSid = NULL, CapturedSid;
+                    ULONG PrimaryGroupIndex;
 
                     _SEH2_TRY
                     {
@@ -2260,9 +2495,25 @@ NtSetInformationToken(IN HANDLE TokenHandle,
                                            &CapturedSid);
                     if (NT_SUCCESS(Status))
                     {
-                        RtlCopySid(RtlLengthSid(CapturedSid),
-                                   Token->PrimaryGroup,
-                                   CapturedSid);
+                        /* Lock the token */
+                        SepAcquireTokenLockExclusive(Token);
+
+                        /* Find the primary group amongst the existing token user and groups */
+                        Status = SepFindPrimaryGroupAndDefaultOwner(Token,
+                                                                    CapturedSid,
+                                                                    NULL,
+                                                                    &PrimaryGroupIndex,
+                                                                    NULL);
+                        if (NT_SUCCESS(Status))
+                        {
+                            /* Found it */
+                            Token->PrimaryGroup = Token->UserAndGroups[PrimaryGroupIndex].Sid;
+                            ExAllocateLocallyUniqueId(&Token->ModifiedId);
+                        }
+
+                        /* Unlock the token */
+                        SepReleaseTokenLock(Token);
+
                         SepReleaseSid(CapturedSid,
                                       PreviousMode,
                                       FALSE);
@@ -2305,36 +2556,87 @@ NtSetInformationToken(IN HANDLE TokenHandle,
                                                &CapturedAcl);
                         if (NT_SUCCESS(Status))
                         {
-                            /* Free the previous dacl if present */
-                            if(Token->DefaultDacl != NULL)
+                            ULONG DynamicLength;
+
+                            /* Lock the token */
+                            SepAcquireTokenLockExclusive(Token);
+
+                            //
+                            // NOTE: So far our dynamic area only contains
+                            // the default dacl, so this makes the following
+                            // code pretty simple. The day where it stores
+                            // other data, the code will require adaptations.
+                            //
+
+                            DynamicLength = Token->DynamicAvailable;
+                            // Add here any other data length present in the dynamic area...
+                            if (Token->DefaultDacl)
+                                DynamicLength += Token->DefaultDacl->AclSize;
+
+                            /* Reallocate the dynamic area if it is too small */
+                            Status = STATUS_SUCCESS;
+                            if ((DynamicLength < CapturedAcl->AclSize) ||
+                                (Token->DynamicPart == NULL))
                             {
-                                ExFreePoolWithTag(Token->DefaultDacl, TAG_TOKEN_ACL);
-                            }
+                                PVOID NewDynamicPart;
 
-                            Token->DefaultDacl = ExAllocatePoolWithTag(PagedPool,
+                                NewDynamicPart = ExAllocatePoolWithTag(PagedPool,
                                                                        CapturedAcl->AclSize,
-                                                                       TAG_TOKEN_ACL);
-                            if (!Token->DefaultDacl)
-                            {
-                                ExFreePoolWithTag(CapturedAcl, TAG_ACL);
-                                Status = STATUS_NO_MEMORY;
+                                                                       TAG_TOKEN_DYNAMIC);
+                                if (NewDynamicPart == NULL)
+                                {
+                                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                                }
+                                else
+                                {
+                                    if (Token->DynamicPart != NULL)
+                                    {
+                                        // RtlCopyMemory(NewDynamicPart, Token->DynamicPart, DynamicLength);
+                                        ExFreePoolWithTag(Token->DynamicPart, TAG_TOKEN_DYNAMIC);
+                                    }
+                                    Token->DynamicPart = NewDynamicPart;
+                                    Token->DynamicAvailable = 0;
+                                }
                             }
                             else
+                            {
+                                Token->DynamicAvailable = DynamicLength - CapturedAcl->AclSize;
+                            }
+
+                            if (NT_SUCCESS(Status))
                             {
                                 /* Set the new dacl */
-                                RtlCopyMemory(Token->DefaultDacl, CapturedAcl, CapturedAcl->AclSize);
-                                ExFreePoolWithTag(CapturedAcl, TAG_ACL);
+                                Token->DefaultDacl = (PVOID)Token->DynamicPart;
+                                RtlCopyMemory(Token->DefaultDacl,
+                                              CapturedAcl,
+                                              CapturedAcl->AclSize);
+
+                                ExAllocateLocallyUniqueId(&Token->ModifiedId);
                             }
+
+                            /* Unlock the token */
+                            SepReleaseTokenLock(Token);
+
+                            ExFreePoolWithTag(CapturedAcl, TAG_ACL);
                         }
                     }
                     else
                     {
-                        /* Clear and free the default dacl if present */
+                        /* Lock the token */
+                        SepAcquireTokenLockExclusive(Token);
+
+                        /* Clear the default dacl if present */
                         if (Token->DefaultDacl != NULL)
                         {
-                            ExFreePoolWithTag(Token->DefaultDacl, TAG_TOKEN_ACL);
+                            Token->DynamicAvailable += Token->DefaultDacl->AclSize;
+                            RtlZeroMemory(Token->DefaultDacl, Token->DefaultDacl->AclSize);
                             Token->DefaultDacl = NULL;
+
+                            ExAllocateLocallyUniqueId(&Token->ModifiedId);
                         }
+
+                        /* Unlock the token */
+                        SepReleaseTokenLock(Token);
                     }
                 }
                 else
@@ -2360,14 +2662,22 @@ NtSetInformationToken(IN HANDLE TokenHandle,
                 }
                 _SEH2_END;
 
-                if (!SeSinglePrivilegeCheck(SeTcbPrivilege,
-                                            PreviousMode))
+                /* Check for TCB privilege */
+                if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
                 {
                     Status = STATUS_PRIVILEGE_NOT_HELD;
                     break;
                 }
 
+                /* Lock the token */
+                SepAcquireTokenLockExclusive(Token);
+
                 Token->SessionId = SessionId;
+                ExAllocateLocallyUniqueId(&Token->ModifiedId);
+
+                /* Unlock the token */
+                SepReleaseTokenLock(Token);
+
                 break;
             }
 
@@ -2387,6 +2697,7 @@ NtSetInformationToken(IN HANDLE TokenHandle,
                 }
                 _SEH2_END;
 
+                /* Check for TCB privilege */
                 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
                 {
                     Status = STATUS_PRIVILEGE_NOT_HELD;
@@ -2396,11 +2707,30 @@ NtSetInformationToken(IN HANDLE TokenHandle,
                 /* Check if it is 0 */
                 if (SessionReference == 0)
                 {
+                    ULONG OldTokenFlags;
+
+                    /* Lock the token */
+                    SepAcquireTokenLockExclusive(Token);
+
                     /* Atomically set the flag in the token */
-                    RtlInterlockedSetBits(&Token->TokenFlags,
-                                          TOKEN_SESSION_NOT_REFERENCED);
+                    OldTokenFlags = RtlInterlockedSetBits(&Token->TokenFlags,
+                                                          TOKEN_SESSION_NOT_REFERENCED);
+                    /*
+                     * If the flag was already set, do not dereference again
+                     * the logon session. Use SessionReference as an indicator
+                     * to know whether to really dereference the session.
+                     */
+                    if (OldTokenFlags == Token->TokenFlags)
+                        SessionReference = ULONG_MAX;
+
+                    /* Unlock the token */
+                    SepReleaseTokenLock(Token);
                 }
 
+                /* Dereference the logon session if needed */
+                if (SessionReference == 0)
+                    SepRmDereferenceLogonSession(&Token->AuthenticationId);
+
                 break;
             }
 
@@ -2469,8 +2799,8 @@ NtSetInformationToken(IN HANDLE TokenHandle,
                 }
                 _SEH2_END;
 
-                if (!SeSinglePrivilegeCheck(SeTcbPrivilege,
-                                            PreviousMode))
+                /* Check for TCB privilege */
+                if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
                 {
                     Status = STATUS_PRIVILEGE_NOT_HELD;
                     break;
@@ -2481,6 +2811,7 @@ NtSetInformationToken(IN HANDLE TokenHandle,
 
                 /* Set the new audit policy */
                 Token->AuditPolicy = AuditPolicy;
+                ExAllocateLocallyUniqueId(&Token->ModifiedId);
 
                 /* Unlock the token */
                 SepReleaseTokenLock(Token);
@@ -2515,12 +2846,13 @@ NtSetInformationToken(IN HANDLE TokenHandle,
                 SepAcquireTokenLockExclusive(Token);
 
                 /* Check if there is no token origin set yet */
-                if ((Token->OriginatingLogonSession.LowPart == 0) &&
-                    (Token->OriginatingLogonSession.HighPart == 0))
+                if (RtlIsZeroLuid(&Token->OriginatingLogonSession))
                 {
                     /* Set the token origin */
                     Token->OriginatingLogonSession =
                         TokenOrigin.OriginatingLogonSession;
+
+                    ExAllocateLocallyUniqueId(&Token->ModifiedId);
                 }
 
                 /* Unlock the token */
@@ -2560,13 +2892,17 @@ Cleanup:
  * NOTE for readers: http://hex.pp.ua/nt/NtDuplicateToken.php is therefore
  * wrong in that regard, while MSDN documentation is correct.
  */
-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)
+_Must_inspect_result_
+__kernel_entry
+NTSTATUS
+NTAPI
+NtDuplicateToken(
+    _In_ HANDLE ExistingTokenHandle,
+    _In_ ACCESS_MASK DesiredAccess,
+    _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
+    _In_ BOOLEAN EffectiveOnly,
+    _In_ TOKEN_TYPE TokenType,
+    _Out_ PHANDLE NewTokenHandle)
 {
     KPROCESSOR_MODE PreviousMode;
     HANDLE hToken;
@@ -2581,7 +2917,9 @@ NtDuplicateToken(IN HANDLE ExistingTokenHandle,
 
     if (TokenType != TokenImpersonation &&
         TokenType != TokenPrimary)
+    {
         return STATUS_INVALID_PARAMETER;
+    }
 
     PreviousMode = KeGetPreviousMode();
 
@@ -2619,6 +2957,7 @@ NtDuplicateToken(IN HANDLE ExistingTokenHandle,
                                        &HandleInformation);
     if (!NT_SUCCESS(Status))
     {
+        DPRINT1("Failed to reference token (Status 0x%lx)\n", Status);
         SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
                                            PreviousMode,
                                            FALSE);
@@ -2712,7 +3051,7 @@ NtAdjustGroupsToken(IN HANDLE TokenHandle,
 
 
 static
-ULONG
+NTSTATUS
 SepAdjustPrivileges(
     _Inout_ PTOKEN Token,
     _In_ BOOLEAN DisableAllPrivileges,
@@ -2720,13 +3059,15 @@ SepAdjustPrivileges(
     _In_ ULONG NewStateCount,
     _Out_opt_ PTOKEN_PRIVILEGES PreviousState,
     _In_ BOOLEAN ApplyChanges,
-    _Out_ PULONG ChangedPrivileges)
+    _Out_ PULONG ChangedPrivileges,
+    _Out_ PBOOLEAN ChangesMade)
 {
     ULONG i, j, PrivilegeCount, ChangeCount, NewAttributes;
 
     /* Count the found privileges and those that need to be changed */
     PrivilegeCount = 0;
     ChangeCount = 0;
+    *ChangesMade = FALSE;
 
     /* Loop all privileges in the token */
     for (i = 0; i < Token->PrivilegeCount; i++)
@@ -2791,16 +3132,17 @@ SepAdjustPrivileges(
                     /* Remove the privilege */
                     SepRemovePrivilegeToken(Token, i);
 
-                    /* Fix the running index */
-                    i--;
+                    *ChangesMade = TRUE;
 
-                    /* Continue with next */
+                    /* Fix the running index and continue with next one */
+                    i--;
                     continue;
                 }
 
                 /* Set the new attributes and update flags */
                 Token->Privileges[i].Attributes = NewAttributes;
                 SepUpdateSinglePrivilegeFlagToken(Token, i);
+                *ChangesMade = TRUE;
             }
 
             /* Increment the change count */
@@ -2841,15 +3183,17 @@ NtAdjustPrivilegesToken(
         PTOKEN_PRIVILEGES PreviousState,
     _When_(PreviousState!=NULL, _Out_) PULONG ReturnLength)
 {
-    PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
+    NTSTATUS Status;
     KPROCESSOR_MODE PreviousMode;
+    PTOKEN Token;
+    PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
     ULONG CapturedCount = 0;
     ULONG CapturedLength = 0;
     ULONG NewStateSize = 0;
     ULONG ChangeCount;
     ULONG RequiredLength;
-    PTOKEN Token;
-    NTSTATUS Status;
+    BOOLEAN ChangesMade = FALSE;
+
     PAGED_CODE();
 
     DPRINT("NtAdjustPrivilegesToken() called\n");
@@ -2936,15 +3280,17 @@ NtAdjustPrivilegesToken(
 
         /* Release the captured privileges */
         if (CapturedPrivileges != NULL)
+        {
             SeReleaseLuidAndAttributesArray(CapturedPrivileges,
                                             PreviousMode,
                                             TRUE);
+        }
 
         return Status;
     }
 
     /* Lock the token */
-    ExEnterCriticalRegionAndAcquireResourceExclusive(Token->TokenLock);
+    SepAcquireTokenLockExclusive(Token);
 
     /* Count the privileges that need to be changed, do not apply them yet */
     Status = SepAdjustPrivileges(Token,
@@ -2953,7 +3299,8 @@ NtAdjustPrivilegesToken(
                                  CapturedCount,
                                  NULL,
                                  FALSE,
-                                 &ChangeCount);
+                                 &ChangeCount,
+                                 &ChangesMade);
 
     /* Check if the caller asked for the previous state */
     if (PreviousState != NULL)
@@ -2992,31 +3339,40 @@ NtAdjustPrivilegesToken(
                                      CapturedCount,
                                      PreviousState,
                                      TRUE,
-                                     &ChangeCount);
+                                     &ChangeCount,
+                                     &ChangesMade);
     }
     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
         /* Do cleanup and return the exception code */
         Status = _SEH2_GetExceptionCode();
+        ChangesMade = TRUE; // Force write.
         _SEH2_YIELD(goto Cleanup);
     }
     _SEH2_END;
 
 Cleanup:
+    /* Touch the token if we made changes */
+    if (ChangesMade)
+        ExAllocateLocallyUniqueId(&Token->ModifiedId);
+
     /* Unlock and dereference the token */
-    ExReleaseResourceAndLeaveCriticalRegion(Token->TokenLock);
+    SepReleaseTokenLock(Token);
     ObDereferenceObject(Token);
 
     /* Release the captured privileges */
     if (CapturedPrivileges != NULL)
+    {
         SeReleaseLuidAndAttributesArray(CapturedPrivileges,
                                         PreviousMode,
                                         TRUE);
+    }
 
     DPRINT ("NtAdjustPrivilegesToken() done\n");
     return Status;
 }
 
+__kernel_entry
 NTSTATUS
 NTAPI
 NtCreateToken(
@@ -3153,6 +3509,12 @@ NtCreateToken(
         return STATUS_BAD_TOKEN_TYPE;
     }
 
+    /* Check for token creation privilege */
+    if (!SeSinglePrivilegeCheck(SeCreateTokenPrivilege, PreviousMode))
+    {
+        return STATUS_PRIVILEGE_NOT_HELD;
+    }
+
     /* Capture the user SID and attributes */
     Status = SeCaptureSidAndAttributesArray(&TokenUser->User,
                                             1,
@@ -3524,8 +3886,8 @@ NtCompareTokens(IN HANDLE FirstTokenHandle,
         IsEqual = TRUE;
     }
 
-    ObDereferenceObject(FirstToken);
     ObDereferenceObject(SecondToken);
+    ObDereferenceObject(FirstToken);
 
     if (NT_SUCCESS(Status))
     {
index f62dc5f..9616432 100644 (file)
@@ -201,6 +201,7 @@ NtCompareTokens(
     _In_ HANDLE SecondTokenHandle,
     _Out_ PBOOLEAN Equal);
 
+__kernel_entry
 NTSYSCALLAPI
 NTSTATUS
 NTAPI