/*++

Copyright (c) Microsoft Corporation. All rights reserved. 

You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.


Module Name:

    psimpers.c

Abstract:

    This module implements the NtImpersonateThread() service.

--*/

#include "psp.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, NtImpersonateThread)
#endif //ALLOC_PRAGMA


NTSTATUS
NtImpersonateThread(
    __in HANDLE ServerThreadHandle,
    __in HANDLE ClientThreadHandle,
    __in PSECURITY_QUALITY_OF_SERVICE SecurityQos
    )

/*++

Routine Description:

    This routine is used to cause the server thread to impersonate the client
    thread.  The impersonation is done according to the specified quality
    of service parameters.



Arguments:

    ServerThreadHandle - Is a handle to the server thread (the impersonator, or
        doing the impersonation).  This handle must be open for
        THREAD_IMPERSONATE access.

    ClientThreadHandle - Is a handle to the Client thread (the impersonatee, or
        one being impersonated).   This handle must be open for
        THREAD_DIRECT_IMPERSONATION access.


    SecurityQos - A pointer to security quality of service information
        indicating what form of impersonation is to be performed.



Return Value:

    STATUS_SUCCESS - Indicates the call completed successfully.


--*/

{


    KPROCESSOR_MODE PreviousMode;
    NTSTATUS Status;
    PETHREAD ClientThread, ServerThread;
    SECURITY_QUALITY_OF_SERVICE CapturedQos;
    SECURITY_CLIENT_CONTEXT ClientSecurityContext;

    //
    // Get previous processor mode and probe and capture arguments if necessary
    //

    PreviousMode = KeGetPreviousMode();

    try {

        if (PreviousMode != KernelMode) {
            ProbeForReadSmallStructure (SecurityQos,
                                        sizeof (SECURITY_QUALITY_OF_SERVICE),
                                        sizeof (ULONG));
        }
        CapturedQos = *SecurityQos;

    } except (ExSystemExceptionFilter ()) {
        return GetExceptionCode ();
    }



    //
    // Reference the client thread, checking for appropriate access.
    //

    Status = ObReferenceObjectByHandle (ClientThreadHandle,           // Handle
                                        THREAD_DIRECT_IMPERSONATION,  // DesiredAccess
                                        PsThreadType,                 // ObjectType
                                        PreviousMode,                 // AccessMode
                                        &ClientThread,                // Object
                                        NULL);                        // GrantedAccess

    if (!NT_SUCCESS (Status)) {
        return Status;
    }

    //
    // Reference the client thread, checking for appropriate access.
    //

    Status = ObReferenceObjectByHandle (ServerThreadHandle,           // Handle
                                        THREAD_IMPERSONATE,           // DesiredAccess
                                        PsThreadType,                 // ObjectType
                                        PreviousMode,                 // AccessMode
                                        &ServerThread,                // Object
                                        NULL);                        // GrantedAccess

    if (!NT_SUCCESS (Status)) {
        ObDereferenceObject (ClientThread);
        return Status;
    }


    //
    // Get the client's security context
    //

    Status = SeCreateClientSecurity (ClientThread,             // ClientThread
                                     &CapturedQos,             // SecurityQos
                                     FALSE,                    // ServerIsRemote
                                     &ClientSecurityContext);  // ClientContext

    if (!NT_SUCCESS (Status)) {
        ObDereferenceObject (ServerThread);
        ObDereferenceObject (ClientThread);
        return Status;
    }


    //
    // Impersonate the client
    //

    Status = SeImpersonateClientEx (&ClientSecurityContext, ServerThread);

    SeDeleteClientSecurity (&ClientSecurityContext);

    //
    // Done.
    //


    ObDereferenceObject (ServerThread);
    ObDereferenceObject (ClientThread);

    return Status ;
}

