Ejemplo n.º 1
0
NTSTATUS
SepRmCallLsa(
    PSEP_WORK_ITEM SepWorkItem
    )
/*++

Routine Description:

    This function sends a command to the LSA via the LSA Reference Monitor
    Server Command LPC Port.  If the command has parameters, they will be
    copied directly into a message structure and sent via LPC, therefore,
    the supplied parameters may not contain any absolute pointers.  A caller
    must remove pointers by "marshalling" them into the buffer CommandParams.

    This function will create a queue of requests.  This is in order to allow
    greater throughput for the majority if its callers.  If a thread enters
    this routine and finds the queue empty, it is the responsibility of that
    thread to service all requests that come in while it is working until the
    queue is empty again.  Other threads that enter will simply hook their work
    item onto the queue and exit.


    To implement a new LSA command, do the following:
    ================================================

    (1)  If the command takes no parameters, just call this routine directly
         and provide an LSA worker routine called Lsap<command>Wrkr.  See
         file lsa\server\lsarm.c for examples

    (2)  If the command takes parameters, provide a routine called
         SepRmSend<command>Command that takes the parameters in unmarshalled
         form and calls SepRmCallLsa() with the command id, marshalled
         parameters, length  of marshalled parameters and pointer to
         optional reply message.  The marshalled parameters are free format:
         the only restriction is that there must be no absolute address
         pointers.  These parameters are all placed in the passed LsaWorkItem
         structure.

    (3)  In file private\inc\ntrmlsa.h, append a command name  to the
         enumerated type LSA_COMMAND_NUMBER defined in file
         private\inc\ntrmlsa.h.  Change the #define for LsapMaximumCommand
         to reference the new command.

    (4)  Add the Lsap<command>Wrkr to the command dispatch table structure
         LsapCommandDispatch[] in file lsarm.c.

    (5)  Add function prototypes to lsap.h and sep.h.


Arguments:

    LsaWorkItem - Supplies a pointer to an SE_LSA_WORK_ITEM containing the
        information to be passed to LSA.  This structure will be freed
        asynchronously by some invocation of this routine, not necessarily
        in the current context.

        !THIS PARAMETER MUST BE ALLOCATED OUT OF NONPAGED POOL!

Return Value:

    NTSTATUS - Result Code.  This is either a result code returned from
        trying to send the command/receive the reply, or a status code
        from the command itself.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    LSA_COMMAND_MESSAGE CommandMessage;
    LSA_REPLY_MESSAGE ReplyMessage;
    PSEP_LSA_WORK_ITEM WorkQueueItem;
    ULONG LocalListLength = 0;
    SIZE_T RegionSize;
    PVOID CopiedCommandParams = NULL;
    PVOID LsaViewCopiedCommandParams = NULL;

    PAGED_CODE();

    UNREFERENCED_PARAMETER( SepWorkItem );
    
    WorkQueueItem = SepWorkListHead();

    KeAttachProcess( &SepRmLsaCallProcess->Pcb );

    while ( WorkQueueItem ) {

        //
        // Construct a message for LPC.  First, fill in the message header
        // fields for LPC, specifying the message type and data sizes for
        // the outgoing CommandMessage and the incoming ReplyMessage.
        //

        CommandMessage.MessageHeader.u2.ZeroInit = 0;
        CommandMessage.MessageHeader.u1.s1.TotalLength =
            ((CSHORT) RM_COMMAND_MESSAGE_HEADER_SIZE +
            (CSHORT) WorkQueueItem->CommandParamsLength);
        CommandMessage.MessageHeader.u1.s1.DataLength =
            CommandMessage.MessageHeader.u1.s1.TotalLength -
            (CSHORT) sizeof(PORT_MESSAGE);

        ReplyMessage.MessageHeader.u2.ZeroInit = 0;
        ReplyMessage.MessageHeader.u1.s1.DataLength = (CSHORT) WorkQueueItem->ReplyBufferLength;
        ReplyMessage.MessageHeader.u1.s1.TotalLength =
            ReplyMessage.MessageHeader.u1.s1.DataLength +
            (CSHORT) sizeof(PORT_MESSAGE);

        //
        // Next, fill in the header info needed by the LSA.
        //

        CommandMessage.CommandNumber = WorkQueueItem->CommandNumber;
        ReplyMessage.ReturnedStatus = STATUS_SUCCESS;

        //
        // Set up the Command Parameters either in the LPC Command Message
        // itself, in the preallocated Lsa shared memory block, or in a
        // specially allocated block.  The parameters are either
        // immediate (i.e. in the WorkQueueItem itself, or are in a buffer
        // pointed to by the address in the WorkQueueItem.
        //

        switch (WorkQueueItem->CommandParamsMemoryType) {

        case SepRmImmediateMemory:

            //
            // The Command Parameters are in the CommandParams buffer
            // in the Work Queue Item.  Just copy them to the corresponding
            // buffer in the CommandMessage buffer.
            //

            CommandMessage.CommandParamsMemoryType = SepRmImmediateMemory;

            RtlCopyMemory(
                CommandMessage.CommandParams,
                &WorkQueueItem->CommandParams,
                WorkQueueItem->CommandParamsLength
                );

            break;

        case SepRmPagedPoolMemory:
        case SepRmUnspecifiedMemory:

            //
            // The Command Parameters are contained in paged pool memory.
            // Since this memory is is not accessible by the LSA, we must
            // copy of them either to the LPC Command Message Block, or
            // into LSA shared memory.
            //

            if (WorkQueueItem->CommandParamsLength <= LSA_MAXIMUM_COMMAND_PARAM_SIZE) {

                //
                // Parameters will fit into the LPC Command Message block.
                //

                CopiedCommandParams = CommandMessage.CommandParams;

                RtlCopyMemory(
                    CopiedCommandParams,
                    WorkQueueItem->CommandParams.BaseAddress,
                    WorkQueueItem->CommandParamsLength
                    );

                CommandMessage.CommandParamsMemoryType = SepRmImmediateMemory;

            } else {

                //
                // Parameters too large for LPC Command Message block.
                // If possible, copy them to the preallocated Lsa Shared
                // Memory block.  If they are too large to fit, copy them
                // to an individually allocated chunk of Shared Virtual
                // Memory.
                //

                if (WorkQueueItem->CommandParamsLength <= SEP_RM_LSA_SHARED_MEMORY_SIZE) {

                    RtlCopyMemory(
                        SepRmState.RmViewPortMemory,
                        WorkQueueItem->CommandParams.BaseAddress,
                        WorkQueueItem->CommandParamsLength
                        );

                    LsaViewCopiedCommandParams = SepRmState.LsaViewPortMemory;
                    CommandMessage.CommandParamsMemoryType = SepRmLsaCommandPortSharedMemory;

                } else {

                    Status = SepAdtCopyToLsaSharedMemory(
                                 SepLsaHandle,
                                 WorkQueueItem->CommandParams.BaseAddress,
                                 WorkQueueItem->CommandParamsLength,
                                 &LsaViewCopiedCommandParams
                                 );

                    if (!NT_SUCCESS(Status)) {

                        //
                        // An error occurred, most likely in allocating
                        // shared virtual memory.  For now, just ignore
                        // the error and discard the Audit Record.  Later,
                        // we may consider generating a warning record
                        // indicating some records lost.
                        //

                        break;

                    }

                    CommandMessage.CommandParamsMemoryType = SepRmLsaCustomSharedMemory;
                }

                //
                // Buffer has been successfully copied to a shared Lsa
                // memory buffer.  Place the address of the buffer valid in
                // the LSA's process context in the Command Message.
                //

                *((PVOID *) CommandMessage.CommandParams) =
                    LsaViewCopiedCommandParams;

                CommandMessage.MessageHeader.u1.s1.TotalLength =
                    ((CSHORT) RM_COMMAND_MESSAGE_HEADER_SIZE +
                    (CSHORT) sizeof( LsaViewCopiedCommandParams ));
                CommandMessage.MessageHeader.u1.s1.DataLength =
                    CommandMessage.MessageHeader.u1.s1.TotalLength -
                    (CSHORT) sizeof(PORT_MESSAGE);
            }

            //
            // Free input command params buffer if Paged Pool.
            //

            if (WorkQueueItem->CommandParamsMemoryType == SepRmPagedPoolMemory) {

                ExFreePool( WorkQueueItem->CommandParams.BaseAddress );
            }

            break;

        default:

            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        if (NT_SUCCESS(Status)) {

            //
            // Send Message to the LSA via the LSA Server Command LPC Port.
            // This must be done in the process in which the handle was created.
            //

            if( WorkQueueItem->CommandNumber == LsapLogonSessionDeletedCommand &&
                WorkQueueItem->ReplyBuffer == NULL
                )
            {
                //
                // send a datagram.
                //

                Status = ZwRequestPort(
                         SepRmState.LsaCommandPortHandle,
                         (PPORT_MESSAGE) &CommandMessage
                         );

                ReplyMessage.ReturnedStatus = STATUS_SUCCESS;

            } else {

                Status = ZwRequestWaitReplyPort(
                         SepRmState.LsaCommandPortHandle,
                         (PPORT_MESSAGE) &CommandMessage,
                         (PPORT_MESSAGE) &ReplyMessage
                         );
            }

            //
            // If the command was successful, copy the data back to the output
            // buffer.
            //

            if (NT_SUCCESS(Status)) {

                //
                // Move output from command (if any) to buffer.  Note that this
                // is done even if the command returns status, because some status
                // values are not errors.
                //

                if (ARGUMENT_PRESENT(WorkQueueItem->ReplyBuffer)) {

                    RtlCopyMemory(
                        WorkQueueItem->ReplyBuffer,
                        ReplyMessage.ReplyBuffer,
                        WorkQueueItem->ReplyBufferLength
                        );
                }

                //
                // Return status from command.
                //

                Status = ReplyMessage.ReturnedStatus;

                if (!NT_SUCCESS(Status)) {
                    KdPrint(("Security: Command sent from RM to LSA returned 0x%lx\n",
                        Status));
                }

            } else {

                KdPrint(("Security: Sending Command RM to LSA failed 0x%lx\n", Status));
            }

            //
            // On return from the LPC call to the LSA, we expect the called
            // LSA worker routine to have copied the Command Parameters
            // buffer (if any).  If a custom shared memory buffer was allocated,
            // free it now.
            //

            if (CommandMessage.CommandParamsMemoryType == SepRmLsaCustomSharedMemory) {

                RegionSize = 0;

                Status = ZwFreeVirtualMemory(
                             SepLsaHandle,
                             (PVOID *) &CommandMessage.CommandParams,
                             &RegionSize,
                             MEM_RELEASE
                             );

                ASSERT(NT_SUCCESS(Status));
            }

        }


        //
        // Clean up.  We must call the cleanup functions on its parameter
        // and then free the used WorkQueueItem itself.
        //

        if ( ARGUMENT_PRESENT( WorkQueueItem->CleanupFunction)) {

            (WorkQueueItem->CleanupFunction)(WorkQueueItem->CleanupParameter);
        }

        //
        // Determine if there is more work to do on this list
        //

        WorkQueueItem = SepDequeueWorkItem();
    }

    KeDetachProcess();

    if ( LocalListLength > SepLsaQueueLength ) {
        SepLsaQueueLength = LocalListLength;
    }

    return Status;
}
Ejemplo n.º 2
0
VOID
NTAPI
IopLogWorker(IN PVOID Parameter)
{
#define IO_ERROR_OBJECT_NAMES_LENGTH    100

    NTSTATUS Status;
    PELF_API_MSG Message;
    PIO_ERROR_LOG_MESSAGE ErrorMessage;
    PLIST_ENTRY ListEntry;
    PERROR_LOG_ENTRY LogEntry;
    PIO_ERROR_LOG_PACKET Packet;
    PCHAR StringBuffer;
    ULONG RemainingLength;
    PDRIVER_OBJECT DriverObject;
    PWCHAR NameString;
    ULONG DriverNameLength, DeviceNameLength;
    UCHAR Buffer[sizeof(OBJECT_NAME_INFORMATION) + IO_ERROR_OBJECT_NAMES_LENGTH];
    POBJECT_NAME_INFORMATION ObjectNameInfo = (POBJECT_NAME_INFORMATION)&Buffer;
    POBJECT_NAME_INFORMATION PoolObjectNameInfo;
    ULONG ReturnedLength, MessageLength;
    ULONG ExtraStringLength;
    PWCHAR p;

    PAGED_CODE();

    UNREFERENCED_PARAMETER(Parameter);

    /* Connect to the port */
    if (!IopConnectLogPort()) return;

    /* Allocate the message */
    Message = ExAllocatePoolWithTag(PagedPool, IO_ERROR_LOG_MESSAGE_LENGTH, TAG_IO);
    if (!Message)
    {
        /* Couldn't allocate, try again */
        IopRestartLogWorker();
        return;
    }

    /* Zero out the message and get the actual I/O structure */
    RtlZeroMemory(Message, sizeof(*Message));
    ErrorMessage = &Message->IoErrorMessage;

    /* Start loop */
    while (TRUE)
    {
        /* Get an entry */
        ListEntry = IopGetErrorLogEntry();
        if (!ListEntry) break;
        LogEntry = CONTAINING_RECORD(ListEntry, ERROR_LOG_ENTRY, ListEntry);

        /* Get pointer to the log packet */
        Packet = (PIO_ERROR_LOG_PACKET)((ULONG_PTR)LogEntry +
                                        sizeof(ERROR_LOG_ENTRY));

        /* Calculate the total length of the message only */
        MessageLength = sizeof(IO_ERROR_LOG_MESSAGE) -
                        sizeof(ERROR_LOG_ENTRY) -
                        sizeof(IO_ERROR_LOG_PACKET) +
                        LogEntry->Size;

        /* Copy the packet */
        RtlCopyMemory(&ErrorMessage->EntryData,
                      Packet,
                      LogEntry->Size - sizeof(ERROR_LOG_ENTRY));

        /* Set the timestamp and time */
        ErrorMessage->TimeStamp = LogEntry->TimeStamp;
        ErrorMessage->Type = IO_TYPE_ERROR_MESSAGE;

        /* Check if this message has any strings */
        if (Packet->NumberOfStrings)
        {
            /* String buffer is after the current strings */
            StringBuffer = (PCHAR)&ErrorMessage->EntryData +
                            Packet->StringOffset;
        }
        else
        {
            /* Otherwise, string buffer is at the end */
            StringBuffer = (PCHAR)ErrorMessage + MessageLength;
        }

        /* Align the buffer */
        StringBuffer = ALIGN_UP_POINTER(StringBuffer, WCHAR);

        /* Set the offset for the driver's name to the current buffer */
        ErrorMessage->DriverNameOffset = (ULONG)(StringBuffer -
                                                (PCHAR)ErrorMessage);

        /* Check how much space we have left for the device string */
        RemainingLength = (ULONG)((ULONG_PTR)Message +
                                  IO_ERROR_LOG_MESSAGE_LENGTH -
                                  (ULONG_PTR)StringBuffer);

        NameString = NULL;
        DriverNameLength = 0; DeviceNameLength = 0;
        PoolObjectNameInfo = NULL;
        ObjectNameInfo = (POBJECT_NAME_INFORMATION)&Buffer;

        /* Now check if there is a driver object */
        DriverObject = LogEntry->DriverObject;
        if (DriverObject)
        {
            /* Check if the driver has a name, and use it if so */
            if (DriverObject->DriverName.Buffer)
            {
                NameString = DriverObject->DriverName.Buffer;
                DriverNameLength = DriverObject->DriverName.Length;
            }
            else
            {
                NameString = NULL;
                DriverNameLength = 0;
            }

            /* Check if there isn't a valid name */
            if (!DriverNameLength)
            {
                /* Query the name directly */
                Status = ObQueryNameString(DriverObject,
                                           ObjectNameInfo,
                                           sizeof(Buffer),
                                           &ReturnedLength);
                if (!NT_SUCCESS(Status) || (ObjectNameInfo->Name.Length == 0))
                {
                    /* We don't have a name */
                    DriverNameLength = 0;
                }
                else
                {
                    NameString = ObjectNameInfo->Name.Buffer;
                    DriverNameLength = ObjectNameInfo->Name.Length;
                }
            }
        }
        else
        {
            /* Use default name */
            NameString = L"Application Popup";
            DriverNameLength = (ULONG)wcslen(NameString) * sizeof(WCHAR);
        }

        /* Check if we have a driver name */
        if (DriverNameLength)
        {
            /* Skip to the end of the driver's name */
            p = &NameString[DriverNameLength / sizeof(WCHAR)];

            /* Now we'll walk backwards and assume the minimum size */
            DriverNameLength = sizeof(WCHAR);
            p--;
            while ((*p != L'\\') && (p != NameString))
            {
                /* No backslash found, keep going */
                p--;
                DriverNameLength += sizeof(WCHAR);
            }

            /* Now we probably hit the backslash itself, skip past it */
            if (*p == L'\\')
            {
                p++;
                DriverNameLength -= sizeof(WCHAR);
            }

            /*
             * Now make sure that the driver name fits in the buffer, minus
             * 3 NULL chars (driver name, device name, and remaining strings),
             * and copy the driver name in the string buffer.
             */
            DriverNameLength = min(DriverNameLength,
                                   RemainingLength - 3 * sizeof(UNICODE_NULL));
            RtlCopyMemory(StringBuffer, p, DriverNameLength);
        }

        /* Null-terminate the driver name */
        *((PWSTR)(StringBuffer + DriverNameLength)) = UNICODE_NULL;
        DriverNameLength += sizeof(WCHAR);

        /* Go to the next string buffer position */
        StringBuffer += DriverNameLength;
        RemainingLength -= DriverNameLength;

        /* Update the string offset */
        ErrorMessage->EntryData.StringOffset =
            (USHORT)((ULONG_PTR)StringBuffer - (ULONG_PTR)ErrorMessage);

        /* Check if we have a device object */
        if (LogEntry->DeviceObject)
        {
            /* We do, query its name */
            Status = ObQueryNameString(LogEntry->DeviceObject,
                                       ObjectNameInfo,
                                       sizeof(Buffer) - DriverNameLength,
                                       &ReturnedLength);
            if (!NT_SUCCESS(Status) || (ObjectNameInfo->Name.Length == 0))
            {
                /* Setup an empty name */
                RtlInitEmptyUnicodeString(&ObjectNameInfo->Name, L"", 0);

                /* Check if we failed because our buffer wasn't large enough */
                if (Status == STATUS_INFO_LENGTH_MISMATCH)
                {
                    /* Then we'll allocate one... we really want this name! */
                    PoolObjectNameInfo = ExAllocatePoolWithTag(PagedPool,
                                                               ReturnedLength,
                                                               TAG_IO);
                    if (PoolObjectNameInfo)
                    {
                        /* Query it again */
                        ObjectNameInfo = PoolObjectNameInfo;
                        Status = ObQueryNameString(LogEntry->DeviceObject,
                                                   ObjectNameInfo,
                                                   ReturnedLength,
                                                   &ReturnedLength);
                        if (NT_SUCCESS(Status))
                        {
                            /* Success, update the information */
                            ObjectNameInfo->Name.Length =
                                IO_ERROR_OBJECT_NAMES_LENGTH - (USHORT)DriverNameLength;
                        }
                    }
                }
            }

            NameString = ObjectNameInfo->Name.Buffer;
            DeviceNameLength = ObjectNameInfo->Name.Length;
        }
        else
        {
            /* No device object, setup an empty name */
            NameString = L"";
            DeviceNameLength = 0;
        }

        /*
         * Now make sure that the device name fits in the buffer, minus
         * 2 NULL chars (device name, and remaining strings), and copy
         * the device name in the string buffer.
         */
        DeviceNameLength = min(DeviceNameLength,
                               RemainingLength - 2 * sizeof(UNICODE_NULL));
        RtlCopyMemory(StringBuffer, NameString, DeviceNameLength);

        /* Null-terminate the device name */
        *((PWSTR)(StringBuffer + DeviceNameLength)) = UNICODE_NULL;
        DeviceNameLength += sizeof(WCHAR);

        /* Free the buffer if we had one */
        if (PoolObjectNameInfo)
        {
            ExFreePoolWithTag(PoolObjectNameInfo, TAG_IO);
            PoolObjectNameInfo = NULL;
        }

        /* Go to the next string buffer position */
        ErrorMessage->EntryData.NumberOfStrings++;
        StringBuffer += DeviceNameLength;
        RemainingLength -= DeviceNameLength;

        /* Check if we have any extra strings */
        if (Packet->NumberOfStrings)
        {
            /* Find out the size of the extra strings */
            ExtraStringLength = LogEntry->Size -
                                sizeof(ERROR_LOG_ENTRY) -
                                Packet->StringOffset;

            /* Round up the length */
            ExtraStringLength = ROUND_UP(ExtraStringLength, sizeof(WCHAR));

            /* Make sure that the extra strings fit in our buffer */
            if (ExtraStringLength > (RemainingLength - sizeof(UNICODE_NULL)))
            {
                /* They wouldn't, so set normalize the length */
                MessageLength -= ExtraStringLength - RemainingLength;
                ExtraStringLength = RemainingLength - sizeof(UNICODE_NULL);
            }

            /* Now copy the extra strings */
            RtlCopyMemory(StringBuffer,
                          (PCHAR)Packet + Packet->StringOffset,
                          ExtraStringLength);

            /* Null-terminate them */
            *((PWSTR)(StringBuffer + ExtraStringLength)) = UNICODE_NULL;
        }

        /* Set the driver name length */
        ErrorMessage->DriverNameLength = (USHORT)DriverNameLength;

        /* Update the message length to include the driver and device names */
        MessageLength += DriverNameLength + DeviceNameLength;
        ErrorMessage->Size = (USHORT)MessageLength;

        /* Now update it again for the size of the actual LPC */
        MessageLength += (FIELD_OFFSET(ELF_API_MSG, IoErrorMessage) -
                          FIELD_OFFSET(ELF_API_MSG, Unknown[0]));

        /* Set the total and data lengths */
        Message->Header.u1.s1.TotalLength =
            (USHORT)(sizeof(PORT_MESSAGE) + MessageLength);
        Message->Header.u1.s1.DataLength = (USHORT)MessageLength;

        /* Send the message */
        Status = ZwRequestPort(IopLogPort, &Message->Header);
        if (!NT_SUCCESS(Status))
        {
            /*
             * An error happened while sending the message on the port.
             * Close the port, requeue the log message on top of the list
             * and restart the worker.
             */
            ZwClose(IopLogPort);
            IopLogPortConnected = FALSE;

            ExInterlockedInsertHeadList(&IopErrorLogListHead,
                                        &LogEntry->ListEntry,
                                        &IopLogListLock);

            IopRestartLogWorker();
            break;
        }

        /* NOTE: The following is basically 'IoFreeErrorLogEntry(Packet)' */

        /* Dereference both objects */
        if (LogEntry->DeviceObject) ObDereferenceObject(LogEntry->DeviceObject);
        if (LogEntry->DriverObject) ObDereferenceObject(LogEntry->DriverObject);

        /* Decrease the total allocation size and free the entry */
        InterlockedExchangeAdd(&IopTotalLogSize, -(LONG)LogEntry->Size);
        ExFreePoolWithTag(LogEntry, TAG_ERROR_LOG);
    }

    /* Free the LPC Message */
    ExFreePoolWithTag(Message, TAG_IO);
}