https://git.reactos.org/?p=reactos.git;a=commitdiff;h=51279c3e44459f120dc4db650264d1d0b69821a3

commit 51279c3e44459f120dc4db650264d1d0b69821a3
Author:     George Bișoc <[email protected]>
AuthorDate: Fri Jun 2 13:00:28 2023 +0200
Commit:     George Bișoc <[email protected]>
CommitDate: Sun Jun 4 11:09:34 2023 +0200

    [NTOS:SE] Refactor NtOpenThreadTokenEx
    
    - Wrap most of the code into a new private routine, SepOpenThreadToken.
    And properly fail gracefully if we fail to open a thread's token instead of 
just keeping going.
    
    - Do not use the same thread object that we have referenced in 
NtOpenThreadTokenEx
    to do a copy of the access token in case we can't open it directly.
    Instead we must reference a new object with full access, solely used for
    the purpose to do our required operations.
    
    - Add debug prints
    
    CORE-18986
---
 ntoskrnl/se/token.c | 375 +++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 281 insertions(+), 94 deletions(-)

diff --git a/ntoskrnl/se/token.c b/ntoskrnl/se/token.c
index d5b7555ac07..c048294c868 100644
--- a/ntoskrnl/se/token.c
+++ b/ntoskrnl/se/token.c
@@ -1101,6 +1101,252 @@ SepFindPrimaryGroupAndDefaultOwner(
     return STATUS_SUCCESS;
 }
 
+/**
+ * @brief
+ * Internal private function that returns an opened handle
+ * of an access token associated with a thread.
+ *
+ * @param[in] Thread
+ * A pointer to a Executive thread. This parameter is used to
+ * validate that the newly obtained thread in this function
+ * hasn't diverged. This could potentially lead to a scenario
+ * that we might get an access token from a different token
+ * which is not what we want. The validation is performed
+ * if the token has to copied and can't be opened directly.
+ *
+ * @param[in] ThreadHandle
+ * A handle to a thread, of which an access token is to be opened
+ * and given from that thread.
+ *
+ * @param[in] ThreadToken
+ * A pointer to an access token associated with the specific thread.
+ * The function assumes that the token is an impersonation one
+ * prior the calling of this function.
+ *
+ * @param[in] DesiredAccess
+ * The desired access rights for the access token.
+ *
+ * @param[in] HandleAttributes
+ * Handle attributes of which they are used for the newly creation
+ * of the opened thread token. The function assumes that they have
+ * been validated prior the calling of this function.
+ *
+ * @param[in] EffectiveOnly
+ * If set to TRUE, the function will copy a new access token with
+ * privileges and groups that are effectively enabled. Any disabled
+ * privilege or group is removed from the copied token. Otherwise
+ * if set to FALSE, the function retains all the enabled and disabled
+ * privielges and groups.
+ *
+ * @param[in] CopyOnOpen
+ * If set to TRUE, it tells the function that the access token cannot
+ * be directly opened due to the security impersonation info of the
+ * associated thread being enforced. In this case the function will
+ * make a copy of the said token by duplicating it. Otherwise if set
+ * to FALSE, the function will just open the access token directly.
+ *
+ * @param[in] ImpersonationLevel
+ * The security impersonation level, at which it is allowed to
+ * access the token.
+ *
+ * @param[in] PreviousMode
+ * The processor request level mode.
+ *
+ * @param[out] OpenedTokenHandle
+ * A pointer to an opened access token handle associated with the
+ * specific thread, returned to the caller. Initially this parameter
+ * is set to NULL and if the function fails to open the thread's token,
+ * it will stay NULL.
+ *
+ * @return
+ * Returns STATUS_SUCCESS if the function has successfully opened the thread's
+ * token. STATUS_OBJECT_TYPE_MISMATCH is returned if the obtained thread object
+ * no longer matches with the other thread that has been obtained previously.
+ * STATUS_NO_TOKEN is returned if the associated thread's process has no
+ * primary access token. A failure NTSTATUS code is returned otherwise.
+ */
+static
+NTSTATUS
+SepOpenThreadToken(
+    _In_ PETHREAD Thread,
+    _In_ HANDLE ThreadHandle,
+    _In_ PTOKEN ThreadToken,
+    _In_ ACCESS_MASK DesiredAccess,
+    _In_ ULONG HandleAttributes,
+    _In_ BOOLEAN EffectiveOnly,
+    _In_ BOOLEAN CopyOnOpen,
+    _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
+    _In_ KPROCESSOR_MODE PreviousMode,
+    _Out_ PHANDLE OpenedTokenHandle)
+{
+    NTSTATUS Status;
+    HANDLE TokenHandle;
+    PETHREAD Thread2;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    PTOKEN NewToken, PrimaryToken;
+    SECURITY_DESCRIPTOR SecurityDescriptor;
+    PACL Dacl;
+
+    PAGED_CODE();
+
+    /* Assume no opened token handle at first */
+    *OpenedTokenHandle = NULL;
+
+    /* Check if we have to do a copy of the token on open or not */
+    if (!CopyOnOpen)
+    {
+        /* Just open the thread's token directly */
+        Status = ObOpenObjectByPointer(ThreadToken,
+                                       HandleAttributes,
+                                       NULL,
+                                       DesiredAccess,
+                                       SeTokenObjectType,
+                                       PreviousMode,
+                                       &TokenHandle);
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("Failed to open the thread's token object (Status 
0x%lx)\n", Status);
+            return Status;
+        }
+
+        /* Give it to caller */
+        *OpenedTokenHandle = TokenHandle;
+        return STATUS_SUCCESS;
+    }
+
+    /*
+     * The caller asks to do a copy of that token whilst it's opened.
+     * Obtain a thread object again but this time we have to obtain
+     * it in our side, kernel mode, and request all the access needed
+     * to do a copy of the token because the original thread only has
+     * query access needed for access token validation.
+     */
+    Status = ObReferenceObjectByHandle(ThreadHandle,
+                                       THREAD_ALL_ACCESS,
+                                       PsThreadType,
+                                       KernelMode,
+                                       (PVOID*)&Thread2,
+                                       NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to reference the object thread (Status 0x%lx)\n", 
Status);
+        return Status;
+    }
+
+    /* Check that one of the threads hasn't diverged */
+    if (Thread != Thread2)
+    {
+        DPRINT1("One of the threads aren't the same (original thread 0x%p, 
thread 0x%p)\n", Thread, Thread2);
+        ObDereferenceObject(Thread2);
+        return STATUS_OBJECT_TYPE_MISMATCH;
+    }
+
+    /* Reference the primary token of the process' thread */
+    PrimaryToken = PsReferencePrimaryToken(Thread2->ThreadsProcess);
+    if (!PrimaryToken)
+    {
+        DPRINT1("Failed to reference the primary token of thread\n");
+        ObDereferenceObject(Thread2);
+        return STATUS_NO_TOKEN;
+    }
+
+    /* Create an impersonation DACL from the tokens we got */
+    Status = SepCreateImpersonationTokenDacl(ThreadToken, PrimaryToken, &Dacl);
+    ObFastDereferenceObject(&Thread2->ThreadsProcess->Token, PrimaryToken);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to create an impersonation token DACL (Status 
0x%lx)\n", Status);
+        ObDereferenceObject(Thread2);
+        return Status;
+    }
+
+    /* Create a security descriptor with the DACL we got */
+    Status = RtlCreateSecurityDescriptor(&SecurityDescriptor,
+                                         SECURITY_DESCRIPTOR_REVISION);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to create a security descriptor (Status 0x%lx)\n", 
Status);
+        ExFreePoolWithTag(Dacl, TAG_ACL);
+        ObDereferenceObject(Thread2);
+        return Status;
+    }
+
+    /* Attach the DACL to that security descriptor */
+    Status = RtlSetDaclSecurityDescriptor(&SecurityDescriptor,
+                                          TRUE,
+                                          Dacl,
+                                          FALSE);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to set the DACL to the security descriptor (Status 
0x%lx)\n", Status);
+        ExFreePoolWithTag(Dacl, TAG_ACL);
+        ObDereferenceObject(Thread2);
+        return Status;
+    }
+
+    /*
+     * Initialize the object attributes for the token we
+     * are going to duplicate.
+     */
+    InitializeObjectAttributes(&ObjectAttributes,
+                               NULL,
+                               HandleAttributes,
+                               NULL,
+                               &SecurityDescriptor);
+
+    /* Duplicate (copy) it now */
+    Status = SepDuplicateToken(ThreadToken,
+                               &ObjectAttributes,
+                               EffectiveOnly,
+                               TokenImpersonation,
+                               ImpersonationLevel,
+                               KernelMode,
+                               &NewToken);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to duplicate the token (Status 0x%lx)\n", Status);
+        ExFreePoolWithTag(Dacl, TAG_ACL);
+        ObDereferenceObject(Thread2);
+        return Status;
+    }
+
+    /* Insert that copied token into the handle now */
+    ObReferenceObject(NewToken);
+    Status = ObInsertObject(NewToken,
+                            NULL,
+                            DesiredAccess,
+                            0,
+                            NULL,
+                            &TokenHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to insert the token object (Status 0x%lx)\n", Status);
+        ExFreePoolWithTag(Dacl, TAG_ACL);
+        ObDereferenceObject(NewToken);
+        ObDereferenceObject(Thread2);
+        return Status;
+    }
+
+    /* We're almost done, free the DACL if we got one */
+    ExFreePoolWithTag(Dacl, TAG_ACL);
+
+    /* Impersonate the client finally */
+    Status = PsImpersonateClient(Thread, NewToken, FALSE, EffectiveOnly, 
ImpersonationLevel);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to impersonate the client (Status 0x%lx)\n", Status);
+        ObDereferenceObject(NewToken);
+        ObDereferenceObject(Thread2);
+        return Status;
+    }
+
+    /* Give the newly opened token handle to caller */
+    *OpenedTokenHandle = TokenHandle;
+    ObDereferenceObject(NewToken);
+    ObDereferenceObject(Thread2);
+    return Status;
+}
+
 /**
  * @brief
  * Subtracts a token in exchange of duplicating a new one.
@@ -2085,13 +2331,10 @@ NtOpenThreadTokenEx(
 {
     PETHREAD Thread;
     HANDLE hToken;
-    PTOKEN Token, NewToken = NULL, PrimaryToken;
+    PTOKEN Token;
     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;
     BOOLEAN RestoreImpersonation = FALSE;
@@ -2100,6 +2343,7 @@ NtOpenThreadTokenEx(
 
     PreviousMode = ExGetPreviousMode();
 
+    /* Ensure that we can give the handle to the caller */
     if (PreviousMode != KernelMode)
     {
         _SEH2_TRY
@@ -2119,138 +2363,81 @@ NtOpenThreadTokenEx(
 
     /*
      * At first open the thread token for information access and verify
-     * that the token associated with thread is valid.
+     * that the token associated with the thread is valid.
      */
-
     Status = ObReferenceObjectByHandle(ThreadHandle, THREAD_QUERY_INFORMATION,
                                        PsThreadType, PreviousMode, 
(PVOID*)&Thread,
                                        NULL);
     if (!NT_SUCCESS(Status))
     {
+        DPRINT1("Failed to reference the object thread (Status 0x%lx)\n", 
Status);
         return Status;
     }
 
+    /* Reference the token from the thread */
     Token = PsReferenceImpersonationToken(Thread, &CopyOnOpen, &EffectiveOnly,
                                           &ImpersonationLevel);
     if (Token == NULL)
     {
+        DPRINT("Failed to reference the thread's impersonation token, thread 
has no token\n");
         ObDereferenceObject(Thread);
         return STATUS_NO_TOKEN;
     }
 
+    /* Ensure the token has no anonymous security */
     if (ImpersonationLevel == SecurityAnonymous)
     {
+        DPRINT1("The thread token has anonymous security, can't open it\n");
         PsDereferenceImpersonationToken(Token);
         ObDereferenceObject(Thread);
         return STATUS_CANT_OPEN_ANONYMOUS;
     }
 
-    /*
-     * Revert to self if OpenAsSelf is specified.
-     */
-
+    /* Revert to self if OpenAsSelf is specified */
     if (OpenAsSelf)
     {
         RestoreImpersonation = PsDisableImpersonation(PsGetCurrentThread(),
                                                       &ImpersonationState);
     }
 
-    if (CopyOnOpen)
-    {
-        PrimaryToken = PsReferencePrimaryToken(Thread->ThreadsProcess);
-
-        Status = SepCreateImpersonationTokenDacl(Token, PrimaryToken, &Dacl);
-
-        ObFastDereferenceObject(&Thread->ThreadsProcess->Token, PrimaryToken);
-
-        if (NT_SUCCESS(Status))
-        {
-            if (Dacl)
-            {
-                Status = RtlCreateSecurityDescriptor(&SecurityDescriptor,
-                                                     
SECURITY_DESCRIPTOR_REVISION);
-                if (!NT_SUCCESS(Status))
-                {
-                    DPRINT1("NtOpenThreadTokenEx(): Failed to create a 
security descriptor (Status 0x%lx)\n", Status);
-                }
-
-                Status = RtlSetDaclSecurityDescriptor(&SecurityDescriptor, 
TRUE, Dacl,
-                                                      FALSE);
-                if (!NT_SUCCESS(Status))
-                {
-                    DPRINT1("NtOpenThreadTokenEx(): Failed to set a DACL to 
the security descriptor (Status 0x%lx)\n", Status);
-                }
-            }
-
-            InitializeObjectAttributes(&ObjectAttributes, NULL, 
HandleAttributes,
-                                       NULL, Dacl ? &SecurityDescriptor : 
NULL);
-
-            Status = SepDuplicateToken(Token, &ObjectAttributes, EffectiveOnly,
-                                       TokenImpersonation, ImpersonationLevel,
-                                       KernelMode, &NewToken);
-            if (!NT_SUCCESS(Status))
-            {
-                DPRINT1("NtOpenThreadTokenEx(): Failed to duplicate the token 
(Status 0x%lx)\n", Status);
-            }
-
-            ObReferenceObject(NewToken);
-            Status = ObInsertObject(NewToken, NULL, DesiredAccess, 0, NULL,
-                                    &hToken);
-            if (!NT_SUCCESS(Status))
-            {
-                DPRINT1("NtOpenThreadTokenEx(): Failed to insert the token 
object (Status 0x%lx)\n", Status);
-            }
-        }
-        else
-        {
-            DPRINT1("NtOpenThreadTokenEx(): Failed to impersonate token from 
DACL (Status 0x%lx)\n", Status);
-        }
-    }
-    else
-    {
-        Status = ObOpenObjectByPointer(Token, HandleAttributes,
-                                       NULL, DesiredAccess, SeTokenObjectType,
-                                       PreviousMode, &hToken);
-        if (!NT_SUCCESS(Status))
-        {
-            DPRINT1("NtOpenThreadTokenEx(): Failed to open the object (Status 
0x%lx)\n", Status);
-        }
-    }
-
-    if (Dacl) ExFreePoolWithTag(Dacl, TAG_ACL);
-
+    /* Call the private function to do the job */
+    Status = SepOpenThreadToken(Thread,
+                                ThreadHandle,
+                                Token,
+                                DesiredAccess,
+                                HandleAttributes,
+                                EffectiveOnly,
+                                CopyOnOpen,
+                                ImpersonationLevel,
+                                PreviousMode,
+                                &hToken);
+
+    /* Restore the impersonation back if needed */
     if (RestoreImpersonation)
     {
         PsRestoreImpersonation(PsGetCurrentThread(), &ImpersonationState);
     }
 
+    /* Dereference the access token and the associated thread */
     ObDereferenceObject(Token);
+    ObDereferenceObject(Thread);
 
-    if (NT_SUCCESS(Status) && CopyOnOpen)
+    if (!NT_SUCCESS(Status))
     {
-        Status = PsImpersonateClient(Thread, NewToken, FALSE, EffectiveOnly, 
ImpersonationLevel);
-        if (!NT_SUCCESS(Status))
-        {
-            DPRINT1("NtOpenThreadTokenEx(): Failed to impersonate the client 
(Status 0x%lx)\n", Status);
-        }
+        DPRINT1("Failed to open the thread's token (Status 0x%lx)\n", Status);
+        return Status;
     }
 
-    if (NewToken) ObDereferenceObject(NewToken);
-
-    ObDereferenceObject(Thread);
-
-    if (NT_SUCCESS(Status))
+    /* Give the opened token handle to the caller */
+    _SEH2_TRY
     {
-        _SEH2_TRY
-        {
-            *TokenHandle = hToken;
-        }
-        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
-        {
-            Status = _SEH2_GetExceptionCode();
-        }
-        _SEH2_END;
+        *TokenHandle = hToken;
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status = _SEH2_GetExceptionCode();
     }
+    _SEH2_END;
 
     return Status;
 }

Reply via email to