/*++

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:

    win32.c

Abstract:

   This module implements the definition of the executive Win32 objects.
   Functions to manage these objects are implemented in win32k.sys.

--*/

#include "exp.h"

//
// Address of windowstation and desktop object type descriptors.
//

POBJECT_TYPE ExWindowStationObjectType;
POBJECT_TYPE ExDesktopObjectType;

PKWIN32_CLOSEMETHOD_CALLOUT ExDesktopCloseProcedureCallout;
PKWIN32_CLOSEMETHOD_CALLOUT ExWindowStationCloseProcedureCallout;
PKWIN32_OPENMETHOD_CALLOUT ExDesktopOpenProcedureCallout;
PKWIN32_OKTOCLOSEMETHOD_CALLOUT ExDesktopOkToCloseProcedureCallout;
PKWIN32_OKTOCLOSEMETHOD_CALLOUT ExWindowStationOkToCloseProcedureCallout;
PKWIN32_DELETEMETHOD_CALLOUT ExDesktopDeleteProcedureCallout;
PKWIN32_DELETEMETHOD_CALLOUT ExWindowStationDeleteProcedureCallout;
PKWIN32_PARSEMETHOD_CALLOUT ExWindowStationParseProcedureCallout;
PKWIN32_OPENMETHOD_CALLOUT ExWindowStationOpenProcedureCallout;

//
// common types for above win32 callouts and parameters
//

typedef PVOID PKWIN32_CALLOUT_PARAMETERS;

typedef
NTSTATUS
(*PKWIN32_CALLOUT) (
    IN PKWIN32_CALLOUT_PARAMETERS
    );

NTSTATUS
ExpWin32SessionCallout(
    IN  PKWIN32_CALLOUT CalloutRoutine,
    IN  PKWIN32_CALLOUT_PARAMETERS Parameters,
    IN  ULONG SessionId,
    OUT PNTSTATUS CalloutStatus  OPTIONAL
    );

VOID
ExpWin32CloseProcedure(
   IN PEPROCESS Process OPTIONAL,
   IN PVOID Object,
   IN ACCESS_MASK GrantedAccess,
   IN ULONG_PTR ProcessHandleCount,
   IN ULONG_PTR SystemHandleCount );

BOOLEAN
ExpWin32OkayToCloseProcedure(
    IN PEPROCESS Process OPTIONAL,
    IN PVOID Object,
    IN HANDLE Handle,
    IN KPROCESSOR_MODE PreviousMode
    );

VOID
ExpWin32DeleteProcedure(
    IN PVOID    Object
    );

NTSTATUS
ExpWin32ParseProcedure (
    IN PVOID ParseObject,
    IN PVOID ObjectType,
    IN OUT PACCESS_STATE AccessState,
    IN KPROCESSOR_MODE AccessMode,
    IN ULONG Attributes,
    IN OUT PUNICODE_STRING CompleteName,
    IN OUT PUNICODE_STRING RemainingName,
    IN OUT PVOID Context OPTIONAL,
    IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
    OUT PVOID *Object
    );

NTSTATUS
ExpWin32OpenProcedure(
    IN OB_OPEN_REASON OpenReason,
    IN PEPROCESS Process OPTIONAL,
    IN PVOID Object,
    IN ACCESS_MASK GrantedAccess,
    IN ULONG HandleCount
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, ExpWin32Initialization)
#pragma alloc_text (PAGE, ExpWin32CloseProcedure)
#pragma alloc_text (PAGE, ExpWin32OkayToCloseProcedure)
#pragma alloc_text (PAGE, ExpWin32DeleteProcedure)
#pragma alloc_text (PAGE, ExpWin32OpenProcedure)
#pragma alloc_text (PAGE, ExpWin32ParseProcedure)
#pragma alloc_text (PAGE, ExpWin32SessionCallout)
#endif


/*
 * windowstation generic mapping
 */
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("INITCONST")
#endif
const GENERIC_MAPPING ExpWindowStationMapping = {
    STANDARD_RIGHTS_READ,
    STANDARD_RIGHTS_WRITE,
    STANDARD_RIGHTS_EXECUTE,
    STANDARD_RIGHTS_REQUIRED
};

/*
 * desktop generic mapping
 */
const GENERIC_MAPPING ExpDesktopMapping = {
    STANDARD_RIGHTS_READ,
    STANDARD_RIGHTS_WRITE,
    STANDARD_RIGHTS_EXECUTE,
    STANDARD_RIGHTS_REQUIRED
};
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg()
#endif

/*++

Routine Description:

    Close Procedure for Win32k windostation and desktop objects

Arguments:
    Defined by OB_CLOSE_METHOD

Return Value:


--*/
VOID ExpWin32CloseProcedure(
   IN PEPROCESS Process OPTIONAL,
   IN PVOID Object,
   IN ACCESS_MASK GrantedAccess,
   IN ULONG_PTR ProcessHandleCount,
   IN ULONG_PTR SystemHandleCount )
{

   //
   // SessionId is the first field in the Win32k Object structure
   //
   ULONG SessionId = *((PULONG)Object);
   WIN32_CLOSEMETHOD_PARAMETERS CloseParams;
   NTSTATUS Status;

   CloseParams.Process = Process;
   CloseParams.Object  =  Object;
   CloseParams.GrantedAccess = GrantedAccess;
   CloseParams.ProcessHandleCount = (ULONG)ProcessHandleCount;
   CloseParams.SystemHandleCount =  (ULONG)SystemHandleCount;

   if ((OBJECT_TO_OBJECT_HEADER(Object)->Type) == ExDesktopObjectType) {

       Status = ExpWin32SessionCallout((PKWIN32_CALLOUT)ExDesktopCloseProcedureCallout,
                                       (PKWIN32_CALLOUT_PARAMETERS)&CloseParams,
                                       SessionId,
                                       NULL);
       ASSERT(NT_SUCCESS(Status));

   } else if ((OBJECT_TO_OBJECT_HEADER(Object)->Type) == ExWindowStationObjectType) {

       Status = ExpWin32SessionCallout((PKWIN32_CALLOUT)ExWindowStationCloseProcedureCallout,
                                       (PKWIN32_CALLOUT_PARAMETERS)&CloseParams,
                                       SessionId,
                                       NULL);
       ASSERT(NT_SUCCESS(Status));

   } else {
      ASSERT((OBJECT_TO_OBJECT_HEADER(Object)->Type) == ExDesktopObjectType || (OBJECT_TO_OBJECT_HEADER(Object)->Type == ExWindowStationObjectType));

   }


}

/*++

Routine Description:

    OkayToClose Procedure for Win32k windostation and desktop objects

Arguments:
    Defined by OB_OKAYTOCLOSE_METHOD


Return Value:


--*/
BOOLEAN ExpWin32OkayToCloseProcedure(
    IN PEPROCESS Process OPTIONAL,
    IN PVOID Object,
    IN HANDLE Handle,
    IN KPROCESSOR_MODE PreviousMode
    )
{
   //
   // SessionId is the first field in the Win32k Object structure
   //
   ULONG SessionId = *((PULONG)Object);
   WIN32_OKAYTOCLOSEMETHOD_PARAMETERS OKToCloseParams;
   NTSTATUS Status, CallStatus = STATUS_UNSUCCESSFUL;

   OKToCloseParams.Process      = Process;
   OKToCloseParams.Object       = Object;
   OKToCloseParams.Handle       = Handle;
   OKToCloseParams.PreviousMode = PreviousMode;

   if (OBJECT_TO_OBJECT_HEADER(Object)->Type == ExDesktopObjectType) {

       Status = ExpWin32SessionCallout((PKWIN32_CALLOUT)ExDesktopOkToCloseProcedureCallout,
                                       (PKWIN32_CALLOUT_PARAMETERS)&OKToCloseParams,
                                       SessionId,
                                       &CallStatus);
       ASSERT(NT_SUCCESS(Status));

   } else if (OBJECT_TO_OBJECT_HEADER(Object)->Type == ExWindowStationObjectType) {

       Status = ExpWin32SessionCallout((PKWIN32_CALLOUT)ExWindowStationOkToCloseProcedureCallout,
                                       (PKWIN32_CALLOUT_PARAMETERS)&OKToCloseParams,
                                       SessionId,
                                       &CallStatus);
       ASSERT(NT_SUCCESS(Status));

   } else {
      ASSERT(OBJECT_TO_OBJECT_HEADER(Object)->Type == ExDesktopObjectType ||
             OBJECT_TO_OBJECT_HEADER(Object)->Type == ExWindowStationObjectType);

   }

   return (BOOLEAN)(NT_SUCCESS(CallStatus));

}


/*++

Routine Description:

    Delete Procedure for Win32k windostation and desktop objects

Arguments:
    Defined by OB_DELETE_METHOD


Return Value:


--*/
VOID ExpWin32DeleteProcedure(
    IN PVOID    Object
    )
{
   //
   // SessionId is the first field in the Win32k Object structure
   //
   ULONG SessionId = *((PULONG)Object);
   WIN32_DELETEMETHOD_PARAMETERS DeleteParams;
   NTSTATUS Status;

   DeleteParams.Object  =  Object;


   if (OBJECT_TO_OBJECT_HEADER(Object)->Type == ExDesktopObjectType) {

       Status = ExpWin32SessionCallout((PKWIN32_CALLOUT)ExDesktopDeleteProcedureCallout,
                                       (PKWIN32_CALLOUT_PARAMETERS)&DeleteParams,
                                       SessionId,
                                       NULL);
       ASSERT(NT_SUCCESS(Status));

   } else if (OBJECT_TO_OBJECT_HEADER(Object)->Type == ExWindowStationObjectType) {

       Status = ExpWin32SessionCallout((PKWIN32_CALLOUT)ExWindowStationDeleteProcedureCallout,
                                       (PKWIN32_CALLOUT_PARAMETERS)&DeleteParams,
                                       SessionId,
                                       NULL);
       ASSERT(NT_SUCCESS(Status));

   } else {
      ASSERT(OBJECT_TO_OBJECT_HEADER(Object)->Type == ExDesktopObjectType ||
             OBJECT_TO_OBJECT_HEADER(Object)->Type == ExWindowStationObjectType);

   }


}

/*++

Routine Description:

    Open Procedure for Win32k desktop objects

Arguments:
    Defined by OB_OPEN_METHOD

Return Value:


--*/


NTSTATUS
ExpWin32OpenProcedure(
    IN OB_OPEN_REASON OpenReason,
    IN PEPROCESS Process OPTIONAL,
    IN PVOID Object,
    IN ACCESS_MASK GrantedAccess,
    IN ULONG HandleCount
    )
{

   //
   // SessionId is the first field in the Win32k Object structure
   //
   ULONG SessionId = *((PULONG)Object);
   WIN32_OPENMETHOD_PARAMETERS OpenParams;
   NTSTATUS Status, CallStatus = STATUS_UNSUCCESSFUL;

   OpenParams.OpenReason = OpenReason;
   OpenParams.Process  =  Process;
   OpenParams.Object = Object;
   OpenParams.GrantedAccess = GrantedAccess;
   OpenParams.HandleCount =  HandleCount;

   
   if ((OBJECT_TO_OBJECT_HEADER(Object)->Type) == ExDesktopObjectType) {

       Status = ExpWin32SessionCallout((PKWIN32_CALLOUT)ExDesktopOpenProcedureCallout,
                                       (PKWIN32_CALLOUT_PARAMETERS)&OpenParams,
                                       SessionId,
                                       &CallStatus);

       ASSERT(NT_SUCCESS(Status));

   } else if ((OBJECT_TO_OBJECT_HEADER(Object)->Type) == ExWindowStationObjectType) {

       Status = ExpWin32SessionCallout((PKWIN32_CALLOUT)ExWindowStationOpenProcedureCallout,
                                       (PKWIN32_CALLOUT_PARAMETERS)&OpenParams,
                                       SessionId,
                                       &CallStatus);
       ASSERT(NT_SUCCESS(Status));

   } else {
      ASSERT((OBJECT_TO_OBJECT_HEADER(Object)->Type) == ExDesktopObjectType || (OBJECT_TO_OBJECT_HEADER(Object)->Type == ExWindowStationObjectType));

   }
   
   return CallStatus;
}

/*++

Routine Description:

    Parse Procedure for Win32k windostation objects

Arguments:
    Defined by OB_PARSE_METHOD


Return Value:


--*/

NTSTATUS ExpWin32ParseProcedure (
    IN PVOID ParseObject,
    IN PVOID ObjectType,
    IN OUT PACCESS_STATE AccessState,
    IN KPROCESSOR_MODE AccessMode,
    IN ULONG Attributes,
    IN OUT PUNICODE_STRING CompleteName,
    IN OUT PUNICODE_STRING RemainingName,
    IN OUT PVOID Context OPTIONAL,
    IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
    OUT PVOID *Object
    )
{

   //
   // SessionId is the first field in the Win32k Object structure
   //
   ULONG SessionId = *((PULONG)ParseObject);
   WIN32_PARSEMETHOD_PARAMETERS ParseParams;
   NTSTATUS Status, CallStatus = STATUS_UNSUCCESSFUL;

   ParseParams.ParseObject = ParseObject;
   ParseParams.ObjectType  =  ObjectType;
   ParseParams.AccessState = AccessState;
   ParseParams.AccessMode = AccessMode;
   ParseParams.Attributes = Attributes;
   ParseParams.CompleteName = CompleteName;
   ParseParams.RemainingName = RemainingName;
   ParseParams.Context = Context;
   ParseParams.SecurityQos = SecurityQos;
   ParseParams.Object = Object;

   //
   // Parse Procedure is only provided for WindowStation objects
   //
   Status = ExpWin32SessionCallout((PKWIN32_CALLOUT)ExWindowStationParseProcedureCallout,
                                   (PKWIN32_CALLOUT_PARAMETERS)&ParseParams,
                                   SessionId,
                                   &CallStatus);
   ASSERT(NT_SUCCESS(Status));

   return CallStatus;

}


BOOLEAN
ExpWin32Initialization (
    )

/*++

Routine Description:

    This function creates the Win32 object type descriptors at system
    initialization and stores the address of the object type descriptor
    in local static storage.

Arguments:

    None.

Return Value:

    A value of TRUE is returned if the Win32 object type descriptors are
    successfully created. Otherwise a value of FALSE is returned.

--*/

{

    OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
    NTSTATUS Status;
    UNICODE_STRING TypeName;

    //
    // Initialize string descriptor.
    //

    RtlInitUnicodeString(&TypeName, L"WindowStation");

    //
    // Create windowstation object type descriptor.
    //

    RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
    ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
    ObjectTypeInitializer.GenericMapping = ExpWindowStationMapping;
    ObjectTypeInitializer.SecurityRequired = TRUE;
    ObjectTypeInitializer.PoolType = NonPagedPool;

    ObjectTypeInitializer.CloseProcedure  = ExpWin32CloseProcedure;
    ObjectTypeInitializer.DeleteProcedure = ExpWin32DeleteProcedure;
    ObjectTypeInitializer.OkayToCloseProcedure = ExpWin32OkayToCloseProcedure;

    ObjectTypeInitializer.ParseProcedure  = ExpWin32ParseProcedure;
    ObjectTypeInitializer.OpenProcedure   = ExpWin32OpenProcedure;

    ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK |
                                              OBJ_PERMANENT |
                                              OBJ_EXCLUSIVE;
    ObjectTypeInitializer.ValidAccessMask = STANDARD_RIGHTS_REQUIRED;
    Status = ObCreateObjectType(&TypeName,
                                &ObjectTypeInitializer,
                                (PSECURITY_DESCRIPTOR)NULL,
                                &ExWindowStationObjectType);

    //
    // If the windowstation object type descriptor was not successfully
    // created, then return a value of FALSE.
    //

    if (!NT_SUCCESS(Status))
        return FALSE;



    //
    // Initialize string descriptor.
    //

    RtlInitUnicodeString(&TypeName, L"Desktop");

    ObjectTypeInitializer.ParseProcedure       = NULL; //Desktop has no Parse Procedure

    //
    // Create windowstation object type descriptor.
    //

    ObjectTypeInitializer.GenericMapping = ExpDesktopMapping;
    Status = ObCreateObjectType(&TypeName,
                                &ObjectTypeInitializer,
                                (PSECURITY_DESCRIPTOR)NULL,
                                &ExDesktopObjectType);


    //
    // If the desktop object type descriptor was successfully created, then
    // return a value of TRUE. Otherwise return a value of FALSE.
    //

    return (BOOLEAN)(NT_SUCCESS(Status));
}



NTSTATUS
ExpWin32SessionCallout(
    IN  PKWIN32_CALLOUT CalloutRoutine,
    IN  PKWIN32_CALLOUT_PARAMETERS Parameters,
    IN  ULONG SessionId,
    OUT PNTSTATUS CalloutStatus  OPTIONAL
    )
/*++

Routine Description:

    This routine calls the specified callout routine in session space, for the
    specified session.

Parameters:

    CalloutRoutine - Callout routine in session space.

    Parameters     - Parameters to pass the callout routine.

    SessionId      - Specifies the ID of the session in which the specified
                     callout routine is to be called.

    CalloutStatus  - Optionally, supplies the address of a variable to receive
                     the NTSTATUS code returned by the callout routine.

Return Value:

    Status code that indicates whether or not the function was successful.

Notes:

    Returns STATUS_NOT_FOUND if the specified session was not found.

--*/
{
    NTSTATUS Status, CallStatus;
    PVOID OpaqueSession;
    KAPC_STATE ApcState;

    PAGED_CODE();

    //
    // Make sure we have all the information we need to deliver notification.
    //
    if (CalloutRoutine == NULL) {
        return STATUS_INVALID_PARAMETER;
    }

    //
    // Make sure the callout routine in session space.
    //
    ASSERT(MmIsSessionAddress((PVOID)CalloutRoutine));

    if ((PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_IN_SESSION) &&
        (SessionId == PsGetCurrentProcessSessionId())) {
        //
        // If the call is from a user mode process, and we are asked to call the
        // current session, call directly.
        //
        CallStatus = (CalloutRoutine)(Parameters);

        //
        // Return the callout status.
        //
        if (ARGUMENT_PRESENT(CalloutStatus)) {
            *CalloutStatus = CallStatus;
        }

        Status = STATUS_SUCCESS;

    } else {
        //
        // Reference the session object for the specified session.
        //
        OpaqueSession = MmGetSessionById(SessionId);
        if (OpaqueSession == NULL) {
            return STATUS_NOT_FOUND;
        }

        //
        // Attach to the specified session.
        //
        Status = MmAttachSession(OpaqueSession, &ApcState);
        if (!NT_SUCCESS(Status)) {
            KdPrintEx((DPFLTR_SYSTEM_ID, DPFLTR_WARNING_LEVEL,
                       "ExpWin32SessionCallout: "
                       "could not attach to 0x%p, session %d for registered notification callout @ 0x%p\n",
                       OpaqueSession,
                       SessionId,
                       CalloutRoutine));
            MmQuitNextSession(OpaqueSession);
            return Status;
        }

        //
        // Dispatch notification to the callout routine.
        //
        CallStatus = (CalloutRoutine)(Parameters);

        //
        // Return the callout status.
        //
        if (ARGUMENT_PRESENT(CalloutStatus)) {
            *CalloutStatus = CallStatus;
        }

        //
        // Detach from the session.
        //
        Status = MmDetachSession(OpaqueSession, &ApcState);
        ASSERT(NT_SUCCESS(Status));

        //
        // Dereference the session object.
        //
        Status = MmQuitNextSession(OpaqueSession);
        ASSERT(NT_SUCCESS(Status));
    }

    return Status;
}

