[NTOS/SE]
[reactos.git] / reactos / ntoskrnl / se / token.c
index 16cbd95..4acfb34 100644 (file)
 #pragma alloc_text(INIT, SepInitializeTokenImplementation)
 #endif
 
+#include <ntlsa.h>
+
+typedef struct _TOKEN_AUDIT_POLICY_INFORMATION
+{
+    ULONG PolicyCount;
+    struct
+    {
+        ULONG Category;
+        UCHAR Value;
+    } Policies[1];
+} TOKEN_AUDIT_POLICY_INFORMATION, *PTOKEN_AUDIT_POLICY_INFORMATION;
+
 /* GLOBALS ********************************************************************/
 
 POBJECT_TYPE SeTokenObjectType = NULL;
@@ -24,6 +36,7 @@ ERESOURCE SepTokenLock;
 
 TOKEN_SOURCE SeSystemTokenSource = {"*SYSTEM*", {0}};
 LUID SeSystemAuthenticationId = SYSTEM_LUID;
+LUID SeAnonymousAuthenticationId = ANONYMOUS_LOGON_LUID;
 
 static GENERIC_MAPPING SepTokenMapping = {
     TOKEN_READ,
@@ -38,11 +51,11 @@ static const INFORMATION_CLASS_INFO SeTokenInformationClass[] = {
     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 ),
+    ICI_SQ_SAME( sizeof(TOKEN_USER),                   sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | 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 ),
+    ICI_SQ_SAME( sizeof(TOKEN_GROUPS),                 sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | 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 ),
+    ICI_SQ_SAME( sizeof(TOKEN_PRIVILEGES),             sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | 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 */
@@ -50,13 +63,13 @@ static const INFORMATION_CLASS_INFO SeTokenInformationClass[] = {
     /* 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 ),
+    ICI_SQ_SAME( sizeof(TOKEN_SOURCE),                 sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | 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 ),
+    ICI_SQ_SAME( sizeof(TOKEN_STATISTICS),             sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE | ICIF_SET_SIZE_VARIABLE ),
     /* TokenRestrictedSids */
     ICI_SQ_SAME( sizeof(TOKEN_GROUPS),                 sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ),
     /* TokenSessionId */
@@ -64,13 +77,13 @@ static const INFORMATION_CLASS_INFO SeTokenInformationClass[] = {
     /* 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 ),
+    ICI_SQ_SAME( sizeof(ULONG),                        sizeof(ULONG), ICIF_SET | 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 ),
+    ICI_SQ_SAME( /* FIXME */0,                         sizeof(ULONG), ICIF_QUERY | ICIF_SET | ICIF_QUERY_SIZE_VARIABLE ),
     /* TokenOrigin */
-    ICI_SQ_SAME( sizeof(TOKEN_ORIGIN),                 sizeof(ULONG), ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE ),
+    ICI_SQ_SAME( sizeof(TOKEN_ORIGIN),                 sizeof(ULONG), ICIF_QUERY | ICIF_SET | ICIF_QUERY_SIZE_VARIABLE ),
 };
 
 /* FUNCTIONS *****************************************************************/
@@ -95,6 +108,8 @@ SepCompareTokens(IN PTOKEN FirstToken,
         }
 
         /* FIXME: Check if every privilege that is present in either token is also present in the other one */
+        DPRINT1("FIXME: Pretending tokens are equal!\n");
+        IsEqual = TRUE;
     }
 
     *Equal = IsEqual;
@@ -218,7 +233,39 @@ SeExchangePrimaryToken(PEPROCESS Process,
     PAGED_CODE();
 
     if (NewToken->TokenType != TokenPrimary) return(STATUS_BAD_TOKEN_TYPE);
-    if (NewToken->TokenInUse) return(STATUS_TOKEN_ALREADY_IN_USE);
+    if (NewToken->TokenInUse)
+    {
+        BOOLEAN IsEqual;
+        NTSTATUS Status;
+
+        /* Maybe we're trying to set the same token */
+        OldToken = PsReferencePrimaryToken(Process);
+        if (OldToken == NewToken)
+        {
+            /* So it's a nop. */
+            *OldTokenP = OldToken;
+            return STATUS_SUCCESS;
+        }
+
+        Status = SepCompareTokens(OldToken, NewToken, &IsEqual);
+        if (!NT_SUCCESS(Status))
+        {
+            *OldTokenP = NULL;
+            PsDereferencePrimaryToken(OldToken);
+            return Status;
+        }
+
+        if (!IsEqual)
+        {
+            *OldTokenP = NULL;
+            PsDereferencePrimaryToken(OldToken);
+            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;
+        return STATUS_SUCCESS;
+    }
 
     /* Mark new token in use */
     NewToken->TokenInUse = 1;
@@ -738,6 +785,7 @@ SepCreateToken(OUT PHANDLE TokenHandle,
     for (i = 0; i < GroupCount; i++)
         uLength += RtlLengthSid(Groups[i].Sid);
 
+    // FIXME: should use the object itself
     AccessToken->UserAndGroups = ExAllocatePoolWithTag(PagedPool,
                                                        uLength,
                                                        TAG_TOKEN_USERS);
@@ -775,7 +823,9 @@ SepCreateToken(OUT PHANDLE TokenHandle,
     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);
@@ -812,18 +862,26 @@ SepCreateToken(OUT PHANDLE TokenHandle,
     /* Update privilege flags */
     SepUpdatePrivilegeFlagsToken(AccessToken);
 
-    AccessToken->DefaultDacl = ExAllocatePoolWithTag(PagedPool,
-                                                     DefaultDacl->AclSize,
-                                                     TAG_TOKEN_ACL);
-    if (AccessToken->DefaultDacl == NULL)
+    if (DefaultDacl != NULL)
     {
-        Status = STATUS_INSUFFICIENT_RESOURCES;
-        goto done;
-    }
+        // FIXME: should use the object itself
+        AccessToken->DefaultDacl = ExAllocatePoolWithTag(PagedPool,
+                                                         DefaultDacl->AclSize,
+                                                         TAG_TOKEN_ACL);
+        if (AccessToken->DefaultDacl == NULL)
+        {
+            Status = STATUS_INSUFFICIENT_RESOURCES;
+            goto done;
+        }
 
-    RtlCopyMemory(AccessToken->DefaultDacl,
-                  DefaultDacl,
-                  DefaultDacl->AclSize);
+        RtlCopyMemory(AccessToken->DefaultDacl,
+                      DefaultDacl,
+                      DefaultDacl->AclSize);
+    }
+    else
+    {
+        AccessToken->DefaultDacl = NULL;
+    }
 
     if (!SystemToken)
     {
@@ -1748,7 +1806,8 @@ NtSetInformationToken(IN HANDLE TokenHandle,
                     }
                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
                     {
-                        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+                        Status = _SEH2_GetExceptionCode();
+                        goto Cleanup;
                     }
                     _SEH2_END;
 
@@ -1787,7 +1846,8 @@ NtSetInformationToken(IN HANDLE TokenHandle,
                     }
                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
                     {
-                        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+                        Status = _SEH2_GetExceptionCode();
+                        goto Cleanup;
                     }
                     _SEH2_END;
 
@@ -1826,7 +1886,8 @@ NtSetInformationToken(IN HANDLE TokenHandle,
                     }
                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
                     {
-                        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+                        Status = _SEH2_GetExceptionCode();
+                        goto Cleanup;
                     }
                     _SEH2_END;
 
@@ -1880,7 +1941,8 @@ NtSetInformationToken(IN HANDLE TokenHandle,
                 }
                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
                 {
-                    _SEH2_YIELD(return _SEH2_GetExceptionCode());
+                    Status = _SEH2_GetExceptionCode();
+                    goto Cleanup;
                 }
                 _SEH2_END;
 
@@ -1895,16 +1957,183 @@ NtSetInformationToken(IN HANDLE TokenHandle,
                 break;
             }
 
+            case TokenSessionReference:
+            {
+                ULONG SessionReference;
+
+                _SEH2_TRY
+                {
+                    /* Buffer size was already verified, no need to check here again */
+                    SessionReference = *(PULONG)TokenInformation;
+                }
+                _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+                {
+                    Status = _SEH2_GetExceptionCode();
+                    goto Cleanup;
+                }
+                _SEH2_END;
+
+                if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
+                {
+                    Status = STATUS_PRIVILEGE_NOT_HELD;
+                    goto Cleanup;
+                }
+
+                /* Check if it is 0 */
+                if (SessionReference == 0)
+                {
+                    /* Atomically set the flag in the token */
+                    RtlInterlockedSetBits(&Token->TokenFlags,
+                                          TOKEN_SESSION_NOT_REFERENCED);
+                }
+
+                break;
+
+            }
+
+
+            case TokenAuditPolicy:
+            {
+                PTOKEN_AUDIT_POLICY_INFORMATION PolicyInformation =
+                    (PTOKEN_AUDIT_POLICY_INFORMATION)TokenInformation;
+                SEP_AUDIT_POLICY AuditPolicy;
+                ULONG i;
+
+                _SEH2_TRY
+                {
+                    ProbeForRead(PolicyInformation,
+                                 FIELD_OFFSET(TOKEN_AUDIT_POLICY_INFORMATION,
+                                              Policies[PolicyInformation->PolicyCount]),
+                                 sizeof(ULONG));
+
+                    /* Loop all policies in the structure */
+                    for (i = 0; i < PolicyInformation->PolicyCount; i++)
+                    {
+                        /* Set the corresponding bits in the packed structure */
+                        switch (PolicyInformation->Policies[i].Category)
+                        {
+                            case AuditCategorySystem:
+                                AuditPolicy.PolicyElements.System = PolicyInformation->Policies[i].Value;
+                                break;
+
+                            case AuditCategoryLogon:
+                                AuditPolicy.PolicyElements.Logon = PolicyInformation->Policies[i].Value;
+                                break;
+
+                            case AuditCategoryObjectAccess:
+                                AuditPolicy.PolicyElements.ObjectAccess = PolicyInformation->Policies[i].Value;
+                                break;
+
+                            case AuditCategoryPrivilegeUse:
+                                AuditPolicy.PolicyElements.PrivilegeUse = PolicyInformation->Policies[i].Value;
+                                break;
+
+                            case AuditCategoryDetailedTracking:
+                                AuditPolicy.PolicyElements.DetailedTracking = PolicyInformation->Policies[i].Value;
+                                break;
+
+                            case AuditCategoryPolicyChange:
+                                AuditPolicy.PolicyElements.PolicyChange = PolicyInformation->Policies[i].Value;
+                                break;
+
+                            case AuditCategoryAccountManagement:
+                                AuditPolicy.PolicyElements.AccountManagement = PolicyInformation->Policies[i].Value;
+                                break;
+
+                            case AuditCategoryDirectoryServiceAccess:
+                                AuditPolicy.PolicyElements.DirectoryServiceAccess = PolicyInformation->Policies[i].Value;
+                                break;
+
+                            case AuditCategoryAccountLogon:
+                                AuditPolicy.PolicyElements.AccountLogon = PolicyInformation->Policies[i].Value;
+                                break;
+                        }
+                    }
+                }
+                _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+                {
+                    Status = _SEH2_GetExceptionCode();
+                    goto Cleanup;
+                }
+                _SEH2_END;
+
+                if (!SeSinglePrivilegeCheck(SeTcbPrivilege,
+                                            PreviousMode))
+                {
+                    Status = STATUS_PRIVILEGE_NOT_HELD;
+                    break;
+                }
+
+                /* Lock the token */
+                SepAcquireTokenLockExclusive(Token);
+
+                /* Set the new audit policy */
+                Token->AuditPolicy = AuditPolicy;
+
+                /* Unlock the token */
+                SepReleaseTokenLock(Token);
+
+                break;
+            }
+
+            case TokenOrigin:
+            {
+                TOKEN_ORIGIN TokenOrigin;
+
+                _SEH2_TRY
+                {
+                    /* Copy the token origin */
+                    TokenOrigin = *(PTOKEN_ORIGIN)TokenInformation;
+                }
+                _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+                {
+                    Status = _SEH2_GetExceptionCode();
+                    goto Cleanup;
+                }
+                _SEH2_END;
+
+                /* Check for TCB privilege */
+                if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
+                {
+                    Status = STATUS_PRIVILEGE_NOT_HELD;
+                    break;
+                }
+
+                /* Lock the token */
+                SepAcquireTokenLockExclusive(Token);
+
+                /* Check if there is no token origin set yet */
+                if ((Token->OriginatingLogonSession.LowPart == 0) &&
+                    (Token->OriginatingLogonSession.HighPart == 0))
+                {
+                    /* Set the token origin */
+                    Token->OriginatingLogonSession =
+                        TokenOrigin.OriginatingLogonSession;
+                }
+
+                /* Unlock the token */
+                SepReleaseTokenLock(Token);
+
+                break;
+            }
+
             default:
             {
-                Status = STATUS_NOT_IMPLEMENTED;
+                DPRINT1("Invalid TokenInformationClass: 0x%lx\n",
+                        TokenInformationClass);
+                Status = STATUS_INVALID_INFO_CLASS;
                 break;
             }
         }
-
+Cleanup:
         ObDereferenceObject(Token);
     }
 
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NtSetInformationToken failed with Status 0x%lx\n", Status);
+    }
+
     return Status;
 }
 
@@ -2376,24 +2605,37 @@ Cleanup:
 
 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)
+NtCreateToken(
+    _Out_ PHANDLE TokenHandle,
+    _In_ ACCESS_MASK DesiredAccess,
+    _In_opt_ 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_opt_ PTOKEN_OWNER TokenOwner,
+    _In_ PTOKEN_PRIMARY_GROUP TokenPrimaryGroup,
+    _In_opt_ PTOKEN_DEFAULT_DACL TokenDefaultDacl,
+    _In_ PTOKEN_SOURCE TokenSource)
 {
     HANDLE hToken;
     KPROCESSOR_MODE PreviousMode;
-    ULONG nTokenPrivileges = 0;
+    ULONG PrivilegeCount, GroupCount;
+    PSID OwnerSid, PrimaryGroupSid;
+    PACL DefaultDacl;
     LARGE_INTEGER LocalExpirationTime = {{0, 0}};
+    LUID LocalAuthenticationId;
+    TOKEN_SOURCE LocalTokenSource;
+    SECURITY_QUALITY_OF_SERVICE LocalSecurityQos;
+    PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
+    PSID_AND_ATTRIBUTES CapturedUser = NULL;
+    PSID_AND_ATTRIBUTES CapturedGroups = NULL;
+    PSID CapturedOwnerSid = NULL;
+    PSID CapturedPrimaryGroupSid = NULL;
+    PACL CapturedDefaultDacl = NULL;
+    ULONG PrivilegesLength, UserLength, GroupsLength;
     NTSTATUS Status;
 
     PAGED_CODE();
@@ -2405,64 +2647,197 @@ NtCreateToken(OUT PHANDLE TokenHandle,
         _SEH2_TRY
         {
             ProbeForWriteHandle(TokenHandle);
+
+            if (ObjectAttributes != NULL)
+            {
+                ProbeForRead(ObjectAttributes,
+                             sizeof(OBJECT_ATTRIBUTES),
+                             sizeof(ULONG));
+                LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService;
+            }
+
             ProbeForRead(AuthenticationId,
                          sizeof(LUID),
                          sizeof(ULONG));
+            LocalAuthenticationId = *AuthenticationId;
+
             LocalExpirationTime = ProbeForReadLargeInteger(ExpirationTime);
+
             ProbeForRead(TokenUser,
                          sizeof(TOKEN_USER),
                          sizeof(ULONG));
+
             ProbeForRead(TokenGroups,
                          sizeof(TOKEN_GROUPS),
                          sizeof(ULONG));
+            GroupCount = TokenGroups->GroupCount;
+
             ProbeForRead(TokenPrivileges,
                          sizeof(TOKEN_PRIVILEGES),
                          sizeof(ULONG));
-            ProbeForRead(TokenOwner,
-                         sizeof(TOKEN_OWNER),
-                         sizeof(ULONG));
+            PrivilegeCount = TokenPrivileges->PrivilegeCount;
+
+            if (TokenOwner != NULL)
+            {
+                ProbeForRead(TokenOwner,
+                             sizeof(TOKEN_OWNER),
+                             sizeof(ULONG));
+                OwnerSid = TokenOwner->Owner;
+            }
+            else
+            {
+                OwnerSid = NULL;
+            }
+
             ProbeForRead(TokenPrimaryGroup,
                          sizeof(TOKEN_PRIMARY_GROUP),
                          sizeof(ULONG));
-            ProbeForRead(TokenDefaultDacl,
-                         sizeof(TOKEN_DEFAULT_DACL),
-                         sizeof(ULONG));
+            PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup;
+
+            if (TokenDefaultDacl != NULL)
+            {
+                ProbeForRead(TokenDefaultDacl,
+                             sizeof(TOKEN_DEFAULT_DACL),
+                             sizeof(ULONG));
+                DefaultDacl = TokenDefaultDacl->DefaultDacl;
+            }
+            else
+            {
+                DefaultDacl = NULL;
+            }
+
             ProbeForRead(TokenSource,
                          sizeof(TOKEN_SOURCE),
                          sizeof(ULONG));
-            nTokenPrivileges = TokenPrivileges->PrivilegeCount;
+            LocalTokenSource = *TokenSource;
         }
         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
             /* Return the exception code */
-            _SEH2_YIELD(return _SEH2_GetExceptionCode());
+            return _SEH2_GetExceptionCode();
         }
         _SEH2_END;
     }
     else
     {
-        nTokenPrivileges = TokenPrivileges->PrivilegeCount;
+        if (ObjectAttributes != NULL)
+            LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService;
+        LocalAuthenticationId = *AuthenticationId;
         LocalExpirationTime = *ExpirationTime;
+        GroupCount = TokenGroups->GroupCount;
+        PrivilegeCount = TokenPrivileges->PrivilegeCount;
+        OwnerSid = TokenOwner ? TokenOwner->Owner : NULL;
+        PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup;
+        DefaultDacl = TokenDefaultDacl ? TokenDefaultDacl->DefaultDacl : NULL;
+        LocalTokenSource = *TokenSource;
     }
 
+    /* Check token type */
+    if ((TokenType < TokenPrimary) ||
+        (TokenType > TokenImpersonation))
+    {
+        return STATUS_BAD_TOKEN_TYPE;
+    }
+
+    /* Capture the user SID and attributes */
+    Status = SeCaptureSidAndAttributesArray(&TokenUser->User,
+                                            1,
+                                            PreviousMode,
+                                            NULL,
+                                            0,
+                                            PagedPool,
+                                            FALSE,
+                                            &CapturedUser,
+                                            &UserLength);
+    if (!NT_SUCCESS(Status))
+    {
+        goto Cleanup;
+    }
+
+    /* Capture the groups SID and attributes array */
+    Status = SeCaptureSidAndAttributesArray(&TokenGroups->Groups[0],
+                                            GroupCount,
+                                            PreviousMode,
+                                            NULL,
+                                            0,
+                                            PagedPool,
+                                            FALSE,
+                                            &CapturedGroups,
+                                            &GroupsLength);
+    if (!NT_SUCCESS(Status))
+    {
+        goto Cleanup;
+    }
+
+    /* Capture privileges */
+    Status = SeCaptureLuidAndAttributesArray(&TokenPrivileges->Privileges[0],
+                                             PrivilegeCount,
+                                             PreviousMode,
+                                             NULL,
+                                             0,
+                                             PagedPool,
+                                             FALSE,
+                                             &CapturedPrivileges,
+                                             &PrivilegesLength);
+    if (!NT_SUCCESS(Status))
+    {
+        goto Cleanup;
+    }
+
+    /* Capture the token owner SID */
+    if (TokenOwner != NULL)
+    {
+        Status = SepCaptureSid(OwnerSid,
+                               PreviousMode,
+                               PagedPool,
+                               FALSE,
+                               &CapturedOwnerSid);
+        if (!NT_SUCCESS(Status))
+        {
+            goto Cleanup;
+        }
+    }
+
+    /* Capture the token primary group SID */
+    Status = SepCaptureSid(PrimaryGroupSid,
+                           PreviousMode,
+                           PagedPool,
+                           FALSE,
+                           &CapturedPrimaryGroupSid);
+    if (!NT_SUCCESS(Status))
+    {
+        goto Cleanup;
+    }
+
+    /* Capture DefaultDacl */
+    if (DefaultDacl != NULL)
+    {
+        Status = SepCaptureAcl(DefaultDacl,
+                               PreviousMode,
+                               NonPagedPool,
+                               FALSE,
+                               &CapturedDefaultDacl);
+    }
+
+    /* Call the internal function */
     Status = SepCreateToken(&hToken,
                             PreviousMode,
                             DesiredAccess,
                             ObjectAttributes,
                             TokenType,
-                            ((PSECURITY_QUALITY_OF_SERVICE)(ObjectAttributes->SecurityQualityOfService))->ImpersonationLevel,
-                            AuthenticationId,
+                            LocalSecurityQos.ImpersonationLevel,
+                            &LocalAuthenticationId,
                             &LocalExpirationTime,
-                            &TokenUser->User,
-                            TokenGroups->GroupCount,
-                            TokenGroups->Groups,
+                            CapturedUser,
+                            GroupCount,
+                            CapturedGroups,
                             0, // FIXME: Should capture
-                            nTokenPrivileges,
-                            TokenPrivileges->Privileges,
-                            TokenOwner->Owner,
-                            TokenPrimaryGroup->PrimaryGroup,
-                            TokenDefaultDacl->DefaultDacl,
-                            TokenSource,
+                            PrivilegeCount,
+                            CapturedPrivileges,
+                            CapturedOwnerSid,
+                            CapturedPrimaryGroupSid,
+                            CapturedDefaultDacl,
+                            &LocalTokenSource,
                             FALSE);
     if (NT_SUCCESS(Status))
     {
@@ -2477,6 +2852,16 @@ NtCreateToken(OUT PHANDLE TokenHandle,
         _SEH2_END;
     }
 
+Cleanup:
+
+    /* Release what we captured */
+    SeReleaseSidAndAttributesArray(CapturedUser, PreviousMode, FALSE);
+    SeReleaseSidAndAttributesArray(CapturedGroups, PreviousMode, FALSE);
+    SeReleaseLuidAndAttributesArray(CapturedPrivileges, PreviousMode, FALSE);
+    SepReleaseSid(CapturedOwnerSid, PreviousMode, FALSE);
+    SepReleaseSid(CapturedPrimaryGroupSid, PreviousMode, FALSE);
+    SepReleaseAcl(CapturedDefaultDacl, PreviousMode, FALSE);
+
     return Status;
 }