windows-nt/Source/XPSP1/NT/base/ntos/lpc/lpcreply.c
2020-09-26 16:20:57 +08:00

1443 lines
38 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
lpcreply.c
Abstract:
Local Inter-Process Communication (LPC) reply system services.
Author:
Steve Wood (stevewo) 15-May-1989
Revision History:
--*/
#include "lpcp.h"
NTSTATUS
LpcpCopyRequestData (
IN BOOLEAN WriteToMessageData,
IN HANDLE PortHandle,
IN PPORT_MESSAGE Message,
IN ULONG DataEntryIndex,
IN PVOID Buffer,
IN SIZE_T BufferSize,
OUT PSIZE_T NumberOfBytesCopied OPTIONAL
);
#if 0
VOID
LpcpAuditInvalidUse (
IN PVOID Context
);
#endif
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,NtReplyPort)
#pragma alloc_text(PAGE,NtReplyWaitReplyPort)
#pragma alloc_text(PAGE,NtReadRequestData)
#pragma alloc_text(PAGE,NtWriteRequestData)
#pragma alloc_text(PAGE,LpcpCopyRequestData)
#pragma alloc_text(PAGE,LpcpValidateClientPort)
#if 0
#pragma alloc_text(PAGE,LpcpAuditInvalidUse)
#endif
ULONG LpcMaxEventLogs = 10;
#define LPCP_PORT_NAME_MAX 256
typedef struct _LPC_WORK_CONTEXT {
WORK_QUEUE_ITEM WorkItem;
PVOID Buffer;
} LPC_WORK_CONTEXT, *PLPC_WORK_CONTEXT;
#endif
//
// The current number of events registered
//
ULONG LpcpEventCounts = 0;
NTSTATUS
NtReplyPort (
IN HANDLE PortHandle,
IN PPORT_MESSAGE ReplyMessage
)
/*++
Routine Description:
A client and server process can send a reply to a previous request
message with the NtReplyPort service:
The Type field of the message is set to LPC_REPLY by the service. If the
MapInfoOffset field of the reply message is non-zero, then the
PORT_MAP_INFORMATION structure it points to will be processed and the
relevant pages in the caller's address space will be unmapped.
The ClientId and MessageId fields of the ReplyMessage structure are used
to identify the thread waiting for this reply. If the target thread is
in fact waiting for this reply message, then the reply message is copied
into the thread's message buffer and the thread's wait is satisfied.
If the thread is not waiting for a reply or is waiting for a reply to
some other MessageId, then the message is placed in the message queue of
the port that is connected to the communication port specified by the
PortHandle parameter and the Type field of the message is set to
LPC_LOST_REPLY.
Arguments:
PortHandle - Specifies the handle of the communication port that the
original message was received from.
ReplyMessage - Specifies a pointer to the reply message to be sent.
The ClientId and MessageId fields determine which thread will
get the reply.
Return Value:
Status code that indicates whether or not the operation was
successful.
--*/
{
KPROCESSOR_MODE PreviousMode;
PLPCP_PORT_OBJECT PortObject;
PORT_MESSAGE CapturedReplyMessage;
NTSTATUS Status;
PLPCP_MESSAGE Msg;
PETHREAD CurrentThread;
PETHREAD WakeupThread;
PAGED_CODE();
CurrentThread = PsGetCurrentThread();
//
// Get previous processor mode and probe output arguments if necessary.
//
PreviousMode = KeGetPreviousMode();
if (PreviousMode != KernelMode) {
try {
ProbeForReadSmallStructure( ReplyMessage,
sizeof( *ReplyMessage ),
sizeof( ULONG ));
CapturedReplyMessage = *ReplyMessage;
} except( EXCEPTION_EXECUTE_HANDLER ) {
return GetExceptionCode();
}
} else {
CapturedReplyMessage = *ReplyMessage;
}
//
// Make sure DataLength is valid with respect to header size and total
// length
//
if ((((CLONG)CapturedReplyMessage.u1.s1.DataLength) + sizeof( PORT_MESSAGE )) >
((CLONG)CapturedReplyMessage.u1.s1.TotalLength)) {
return STATUS_INVALID_PARAMETER;
}
//
// Make sure the user didn't give us a bogus reply message id
//
if (CapturedReplyMessage.MessageId == 0) {
return STATUS_INVALID_PARAMETER;
}
//
// Reference the port object by handle
//
Status = LpcpReferencePortObject( PortHandle,
0,
PreviousMode,
&PortObject );
if (!NT_SUCCESS( Status )) {
Status = ObReferenceObjectByHandle( PortHandle,
0,
LpcWaitablePortObjectType,
PreviousMode,
&PortObject,
NULL );
if ( !NT_SUCCESS( Status ) ) {
return Status;
}
}
//
// Validate the message length
//
if (((ULONG)CapturedReplyMessage.u1.s1.TotalLength > PortObject->MaxMessageLength) ||
((ULONG)CapturedReplyMessage.u1.s1.TotalLength <= (ULONG)CapturedReplyMessage.u1.s1.DataLength)) {
ObDereferenceObject( PortObject );
return STATUS_PORT_MESSAGE_TOO_LONG;
}
//
// Translate the ClientId from the connection request into a thread
// pointer. This is a referenced pointer to keep the thread from
// evaporating out from under us.
//
Status = PsLookupProcessThreadByCid( &CapturedReplyMessage.ClientId,
NULL,
&WakeupThread );
if (!NT_SUCCESS( Status )) {
ObDereferenceObject( PortObject );
return Status;
}
//
// Acquire the mutex that guards the LpcReplyMessage field of the thread
// and get the pointer to the message that the thread is waiting for a
// reply to.
//
Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( CapturedReplyMessage.u1.s1.TotalLength );
if (Msg == NULL) {
ObDereferenceObject( WakeupThread );
ObDereferenceObject( PortObject );
return STATUS_NO_MEMORY;
}
LpcpAcquireLpcpLockByThread(CurrentThread);
//
// See if the thread is waiting for a reply to the message specified on
// this call. If not then a bogus message has been specified, so
// release the mutex, dereference the thread and return failure.
//
// We also fail this request if the caller isn't replying to a request
// message. For example, if the caller is replying to a connection
// request
//
if ((WakeupThread->LpcReplyMessageId != CapturedReplyMessage.MessageId)
||
((LpcpGetThreadMessage(WakeupThread) != NULL) &&
(LpcpGetThreadMessage(WakeupThread)->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_REQUEST)
||
(!LpcpValidateClientPort(WakeupThread, PortObject, LPCP_VALIDATE_REASON_REPLY)) ) {
LpcpPrint(( "%s Attempted reply to Thread %lx (%s)\n",
PsGetCurrentProcess()->ImageFileName,
WakeupThread,
THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
LpcpPrint(( "failed. MessageId == %u Client Id: %x.%x\n",
CapturedReplyMessage.MessageId,
CapturedReplyMessage.ClientId.UniqueProcess,
CapturedReplyMessage.ClientId.UniqueThread ));
LpcpPrint(( " Thread MessageId == %u Client Id: %x.%x\n",
WakeupThread->LpcReplyMessageId,
WakeupThread->Cid.UniqueProcess,
WakeupThread->Cid.UniqueThread ));
#if DBG
if (LpcpStopOnReplyMismatch) {
DbgBreakPoint();
}
#endif
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
ObDereferenceObject( WakeupThread );
ObDereferenceObject( PortObject );
return STATUS_REPLY_MESSAGE_MISMATCH;
}
//
// Copy the reply message to the request message buffer. Do this before
// we actually fiddle with the wakeup threads fields. Otherwise we
// could mess up its state
//
try {
LpcpMoveMessage( &Msg->Request,
&CapturedReplyMessage,
(ReplyMessage + 1),
LPC_REPLY,
NULL );
} except( EXCEPTION_EXECUTE_HANDLER ) {
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
ObDereferenceObject( WakeupThread );
ObDereferenceObject( PortObject );
return GetExceptionCode();
}
//
// At this point we know the thread is waiting for our reply
//
LpcpTrace(( "%s Sending Reply Msg %lx (%u, %x) [%08x %08x %08x %08x] to Thread %lx (%s)\n",
PsGetCurrentProcess()->ImageFileName,
Msg,
CapturedReplyMessage.MessageId,
CapturedReplyMessage.u2.s2.DataInfoOffset,
*((PULONG)(Msg+1)+0),
*((PULONG)(Msg+1)+1),
*((PULONG)(Msg+1)+2),
*((PULONG)(Msg+1)+3),
WakeupThread,
THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
//
// Locate and free the message from the port. This call use to
// test for (CapturedReplyMessage.u2.s2.DataInfoOffset != 0) as a
// prerequisite for doing the call.
//
LpcpFreeDataInfoMessage( PortObject,
CapturedReplyMessage.MessageId,
CapturedReplyMessage.CallbackId );
//
// Add an extra reference so LpcExitThread does not evaporate the
// pointer before we get to the wait below
//
ObReferenceObject( WakeupThread );
//
// Release the mutex that guards the LpcReplyMessage field after marking
// message as being replied to.
//
Msg->RepliedToThread = WakeupThread;
WakeupThread->LpcReplyMessageId = 0;
WakeupThread->LpcReplyMessage = (PVOID)Msg;
//
// Remove the thread from the reply rundown list as we are sending the
// reply.
//
if (!WakeupThread->LpcExitThreadCalled && !IsListEmpty( &WakeupThread->LpcReplyChain )) {
RemoveEntryList( &WakeupThread->LpcReplyChain );
InitializeListHead( &WakeupThread->LpcReplyChain );
}
if ((CurrentThread->LpcReceivedMsgIdValid) &&
(CurrentThread->LpcReceivedMessageId == CapturedReplyMessage.MessageId)) {
CurrentThread->LpcReceivedMessageId = 0;
CurrentThread->LpcReceivedMsgIdValid = FALSE;
}
LpcpReleaseLpcpLock();
//
// Wake up the thread that is waiting for an answer to its request
// inside of NtRequestWaitReplyPort or NtReplyWaitReplyPort. That
// will dereference itself when it wakes up.
//
KeReleaseSemaphore( &WakeupThread->LpcReplySemaphore,
0,
1L,
FALSE );
ObDereferenceObject( WakeupThread );
//
// Dereference port object and return the system service status.
//
ObDereferenceObject( PortObject );
return Status;
}
NTSTATUS
NtReplyWaitReplyPort (
IN HANDLE PortHandle,
IN OUT PPORT_MESSAGE ReplyMessage
)
/*++
Routine Description:
A client and server process can send a reply to a previous message and
block waiting for a reply using the NtReplyWaitReplyPort service:
This service works the same as NtReplyPort, except that after delivering
the reply message, it blocks waiting for a reply to a previous message.
When the reply is received, it will be placed in the location specified
by the ReplyMessage parameter.
Arguments:
PortHandle - Specifies the handle of the communication port that the
original message was received from.
ReplyMessage - Specifies a pointer to the reply message to be sent.
The ClientId and MessageId fields determine which thread will
get the reply. This buffer also receives any reply that comes
back from the wait.
Return Value:
Status code that indicates whether or not the operation was
successful.
--*/
{
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status;
PLPCP_PORT_OBJECT PortObject;
PORT_MESSAGE CapturedReplyMessage;
PLPCP_MESSAGE Msg;
PETHREAD CurrentThread;
PETHREAD WakeupThread;
PLPCP_PORT_OBJECT RundownPort;
PAGED_CODE();
CurrentThread = PsGetCurrentThread();
//
// Get previous processor mode and probe output arguments if necessary.
//
PreviousMode = KeGetPreviousMode();
if (PreviousMode != KernelMode) {
try {
ProbeForWriteSmallStructure( ReplyMessage,
sizeof( *ReplyMessage ),
sizeof( ULONG ));
CapturedReplyMessage = *ReplyMessage;
} except( EXCEPTION_EXECUTE_HANDLER ) {
return GetExceptionCode();
}
} else {
CapturedReplyMessage = *ReplyMessage;
}
//
// Make sure DataLength is valid with respect to header size and total length
//
if ((((CLONG)CapturedReplyMessage.u1.s1.DataLength) + sizeof( PORT_MESSAGE )) >
((CLONG)CapturedReplyMessage.u1.s1.TotalLength)) {
return STATUS_INVALID_PARAMETER;
}
//
// Make sure the user didn't give us a bogus reply message id
//
if (CapturedReplyMessage.MessageId == 0) {
return STATUS_INVALID_PARAMETER;
}
//
// Reference the communication port object by handle. Return status if
// unsuccessful.
//
Status = LpcpReferencePortObject( PortHandle,
0,
PreviousMode,
&PortObject );
if (!NT_SUCCESS( Status )) {
return Status;
}
//
// Validate the message length
//
if (((ULONG)CapturedReplyMessage.u1.s1.TotalLength > PortObject->MaxMessageLength) ||
((ULONG)CapturedReplyMessage.u1.s1.TotalLength <= (ULONG)CapturedReplyMessage.u1.s1.DataLength)) {
ObDereferenceObject( PortObject );
return STATUS_PORT_MESSAGE_TOO_LONG;
}
//
// Translate the ClientId from the connection request into a
// thread pointer. This is a referenced pointer to keep the thread
// from evaporating out from under us.
//
Status = PsLookupProcessThreadByCid( &CapturedReplyMessage.ClientId,
NULL,
&WakeupThread );
if (!NT_SUCCESS( Status )) {
ObDereferenceObject( PortObject );
return Status;
}
//
// Acquire the mutex that guards the LpcReplyMessage field of
// the thread and get the pointer to the message that the thread
// is waiting for a reply to.
//
Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( CapturedReplyMessage.u1.s1.TotalLength );
if (Msg == NULL) {
ObDereferenceObject( WakeupThread );
ObDereferenceObject( PortObject );
return STATUS_NO_MEMORY;
}
LpcpAcquireLpcpLockByThread(CurrentThread);
//
// See if the thread is waiting for a reply to the message
// specified on this call. If not then a bogus message
// has been specified, so release the mutex, dereference the thread
// and return failure.
//
// We also fail this request if the caller isn't replying to a request
// message. For example, if the caller is replying to a connection
// request
//
if ((WakeupThread->LpcReplyMessageId != CapturedReplyMessage.MessageId)
||
((LpcpGetThreadMessage(WakeupThread) != NULL) &&
(LpcpGetThreadMessage(WakeupThread)->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_REQUEST)
||
(!LpcpValidateClientPort(WakeupThread, PortObject, LPCP_VALIDATE_REASON_REPLY)) ) {
LpcpPrint(( "%s Attempted reply wait reply to Thread %lx (%s)\n",
PsGetCurrentProcess()->ImageFileName,
WakeupThread,
THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
LpcpPrint(( "failed. MessageId == %u Client Id: %x.%x\n",
CapturedReplyMessage.MessageId,
CapturedReplyMessage.ClientId.UniqueProcess,
CapturedReplyMessage.ClientId.UniqueThread ));
LpcpPrint(( " Thread MessageId == %u Client Id: %x.%x\n",
WakeupThread->LpcReplyMessageId,
WakeupThread->Cid.UniqueProcess,
WakeupThread->Cid.UniqueThread ));
#if DBG
if (LpcpStopOnReplyMismatch) {
DbgBreakPoint();
}
#endif
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
ObDereferenceObject( WakeupThread );
ObDereferenceObject( PortObject );
return STATUS_REPLY_MESSAGE_MISMATCH;
}
//
// Copy the reply message to the request message buffer. Do this before
// we actually fiddle with the wakeup threads fields. Otherwise we
// could mess up its state
//
try {
LpcpMoveMessage( &Msg->Request,
&CapturedReplyMessage,
(ReplyMessage + 1),
LPC_REPLY,
NULL );
} except( EXCEPTION_EXECUTE_HANDLER ) {
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
ObDereferenceObject( WakeupThread );
ObDereferenceObject( PortObject );
return (Status = GetExceptionCode());
}
//
// At this point we know the thread is waiting for our reply
//
LpcpTrace(( "%s Sending Reply Wait Reply Msg %lx (%u, %x) [%08x %08x %08x %08x] to Thread %lx (%s)\n",
PsGetCurrentProcess()->ImageFileName,
Msg,
CapturedReplyMessage.MessageId,
CapturedReplyMessage.u2.s2.DataInfoOffset,
*((PULONG)(Msg+1)+0),
*((PULONG)(Msg+1)+1),
*((PULONG)(Msg+1)+2),
*((PULONG)(Msg+1)+3),
WakeupThread,
THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
//
// Locate and free the message from the port. This call use to
// test for (CapturedReplyMessage.u2.s2.DataInfoOffset != 0) as a
// prerequisite for doing the call.
//
LpcpFreeDataInfoMessage( PortObject,
CapturedReplyMessage.MessageId,
CapturedReplyMessage.CallbackId );
//
// Add an extra reference so LpcExitThread does not evaporate
// the pointer before we get to the wait below
//
ObReferenceObject( WakeupThread );
//
// Release the mutex that guards the LpcReplyMessage field
// after marking message as being replied to.
//
Msg->RepliedToThread = WakeupThread;
WakeupThread->LpcReplyMessageId = 0;
WakeupThread->LpcReplyMessage = (PVOID)Msg;
//
// Remove the thread from the reply rundown list as we are sending the reply.
//
if (!WakeupThread->LpcExitThreadCalled && !IsListEmpty( &WakeupThread->LpcReplyChain )) {
RemoveEntryList( &WakeupThread->LpcReplyChain );
InitializeListHead( &WakeupThread->LpcReplyChain );
}
//
// Set ourselves up to get the following reply
//
CurrentThread->LpcReplyMessageId = CapturedReplyMessage.MessageId;
CurrentThread->LpcReplyMessage = NULL;
if ((CurrentThread->LpcReceivedMsgIdValid) &&
(CurrentThread->LpcReceivedMessageId == CapturedReplyMessage.MessageId)) {
CurrentThread->LpcReceivedMessageId = 0;
CurrentThread->LpcReceivedMsgIdValid = FALSE;
}
//
// Insert the current thread into the rundown queue
//
if ((PortObject->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
RundownPort = PortObject->ConnectedPort;
} else {
RundownPort = PortObject;
}
InsertTailList( &RundownPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
//
// Save the port context in the current thread, because
// it waits a reply from the same message
//
LpcpSetPortToThread(CurrentThread, PortObject);
LpcpReleaseLpcpLock();
//
// Wake up the thread that is waiting for an answer to its request
// inside of NtRequestWaitReplyPort or NtReplyWaitReplyPort. That
// will dereference itself when it wakes up.
//
KeReleaseSemaphore( &WakeupThread->LpcReplySemaphore,
1,
1,
FALSE );
ObDereferenceObject( WakeupThread );
//
// And wait for a reply
//
Status = KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
Executive,
PreviousMode,
FALSE,
NULL );
if (Status == STATUS_USER_APC) {
//
// if the semaphore is signaled, then clear it
//
if (KeReadStateSemaphore( &CurrentThread->LpcReplySemaphore )) {
KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
WrExecutive,
KernelMode,
FALSE,
NULL );
Status = STATUS_SUCCESS;
}
}
//
// Remove the thread from the reply rundown list in case we did not wakeup due to
// a reply
//
LpcpAcquireLpcpLockByThread(CurrentThread);
if (!IsListEmpty( &CurrentThread->LpcReplyChain )) {
RemoveEntryList( &CurrentThread->LpcReplyChain );
InitializeListHead( &CurrentThread->LpcReplyChain );
}
//
// If the wait succeeded, copy the reply to the reply buffer.
//
if (Status == STATUS_SUCCESS) {
//
// Acquire the mutex that guards the request message queue. Remove
// the request message from the list of messages being processed and
// free the message back to the queue's zone. If the zone's free
// list was zero before freeing this message then pulse the free
// event after free the message so that threads waiting to allocate
// a request message buffer will wake up. Finally, release the mutex
// and return the system service status.
//
Msg = LpcpGetThreadMessage(CurrentThread);
CurrentThread->LpcReplyMessage = NULL;
#if DBG
if (Msg != NULL) {
LpcpTrace(( "%s Got Reply Msg %lx (%u) [%08x %08x %08x %08x] for Thread %lx (%s)\n",
PsGetCurrentProcess()->ImageFileName,
Msg,
Msg->Request.MessageId,
*((PULONG)(Msg+1)+0),
*((PULONG)(Msg+1)+1),
*((PULONG)(Msg+1)+2),
*((PULONG)(Msg+1)+3),
CurrentThread,
THREAD_TO_PROCESS( CurrentThread )->ImageFileName ));
if (!IsListEmpty( &Msg->Entry )) {
LpcpTrace(( "Reply Msg %lx has non-empty list entry\n", Msg ));
}
}
#endif
LpcpReleaseLpcpLock();
if (Msg != NULL) {
try {
LpcpMoveMessage( ReplyMessage,
&Msg->Request,
(&Msg->Request) + 1,
0,
NULL );
} except( EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
}
//
// Acquire the LPC mutex and decrement the reference count for the
// message. If the reference count goes to zero the message will be
// deleted.
//
LpcpAcquireLpcpLockByThread(CurrentThread);
if (Msg->RepliedToThread != NULL) {
ObDereferenceObject( Msg->RepliedToThread );
Msg->RepliedToThread = NULL;
}
LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
} else {
Status = STATUS_LPC_REPLY_LOST;
}
}
else {
LpcpReleaseLpcpLock();
}
ObDereferenceObject( PortObject );
return Status;
}
NTSTATUS
NtReadRequestData (
IN HANDLE PortHandle,
IN PPORT_MESSAGE Message,
IN ULONG DataEntryIndex,
OUT PVOID Buffer,
IN SIZE_T BufferSize,
OUT PSIZE_T NumberOfBytesRead OPTIONAL
)
/*++
Routine Description:
This routine is used to copy data from a port message into the user
supplied buffer.
Arguments:
PortHandle - Supplies the port from which the message is being read
Message - Supplies the message that we are trying to read
DataEntryIndex - Supplies the index of the port data entry in the
preceeding message that we are reading
Buffer - Supplies the location into which the data is to be read
BufferSize - Supplies the size, in bytes, of the preceeding buffer
NumberOfBytesRead - Optionally returns the number of bytes read into
the buffer
Return Value:
NTSTATUS - An appropriate status value
--*/
{
NTSTATUS status;
PAGED_CODE();
status = LpcpCopyRequestData( FALSE,
PortHandle,
Message,
DataEntryIndex,
Buffer,
BufferSize,
NumberOfBytesRead );
return status;
}
NTSTATUS
NtWriteRequestData (
IN HANDLE PortHandle,
IN PPORT_MESSAGE Message,
IN ULONG DataEntryIndex,
IN PVOID Buffer,
IN SIZE_T BufferSize,
OUT PSIZE_T NumberOfBytesWritten OPTIONAL
)
/*++
Routine Description:
This routine is used to copy data from the user supplied buffer into the
port message
Arguments:
PortHandle - Supplies the port into which the message is being written
Message - Supplies the message that we are trying to write
DataEntryIndex - Supplies the index of the port data entry in the
preceeding message that we are writing
Buffer - Supplies the location into which the data is to be written
BufferSize - Supplies the size, in bytes, of the preceeding buffer
NumberOfBytesWritten - Optionally returns the number of bytes written from
the buffer
Return Value:
NTSTATUS - An appropriate status value
--*/
{
NTSTATUS status;
PAGED_CODE();
status = LpcpCopyRequestData( TRUE,
PortHandle,
Message,
DataEntryIndex,
Buffer,
BufferSize,
NumberOfBytesWritten );
return status;
}
//
// Local support routine
//
NTSTATUS
LpcpCopyRequestData (
IN BOOLEAN WriteToMessageData,
IN HANDLE PortHandle,
IN PPORT_MESSAGE Message,
IN ULONG DataEntryIndex,
IN PVOID Buffer,
IN SIZE_T BufferSize,
OUT PSIZE_T NumberOfBytesCopied OPTIONAL
)
/*++
Routine Description:
This routine will copy data to or from the user supplied buffer and the
port message data information buffer
Arguments:
WriteToMessageData - TRUE if the data is to be copied from the user buffer
to the message and FALSE otherwise
PortHandle - Supplies the port into which the message is being manipulated
Message - Supplies the message that we are trying to manipulate
DataEntryIndex - Supplies the index of the port data entry in the
preceeding message that we are transferring
Buffer - Supplies the location into which the data is to be transfered
BufferSize - Supplies the size, in bytes, of the preceeding buffer
NumberOfBytesRead - Optionally returns the number of bytes transfered from
the buffer
Return Value:
NTSTATUS - An appropriate status value
--*/
{
KPROCESSOR_MODE PreviousMode;
PLPCP_PORT_OBJECT PortObject;
PLPCP_MESSAGE Msg;
NTSTATUS Status;
PETHREAD ClientThread;
PPORT_DATA_INFORMATION DataInfo;
PPORT_DATA_ENTRY DataEntry;
PORT_MESSAGE CapturedMessage;
PORT_DATA_ENTRY CapturedDataEntry;
SIZE_T BytesCopied;
PAGED_CODE();
//
// Get previous processor mode and probe output arguments if necessary.
//
PreviousMode = KeGetPreviousMode();
if (PreviousMode != KernelMode) {
try {
//
// We are either reading or writing the user buffer
//
if (WriteToMessageData) {
ProbeForRead( Buffer,
BufferSize,
1 );
} else {
ProbeForWrite( Buffer,
BufferSize,
1 );
}
ProbeForReadSmallStructure( Message,
sizeof( *Message ),
sizeof( ULONG ));
CapturedMessage = *Message;
if (ARGUMENT_PRESENT( NumberOfBytesCopied )) {
ProbeForWriteUlong_ptr( NumberOfBytesCopied );
}
} except( EXCEPTION_EXECUTE_HANDLER ) {
return GetExceptionCode();
}
} else {
CapturedMessage = *Message;
}
//
// The message better have at least one data entry
//
if (CapturedMessage.u2.s2.DataInfoOffset == 0) {
return STATUS_INVALID_PARAMETER;
}
//
// Reference the port object by handle
//
Status = LpcpReferencePortObject( PortHandle,
0,
PreviousMode,
&PortObject );
if (!NT_SUCCESS( Status )) {
return Status;
}
//
// Translate the ClientId from the connection request into a
// thread pointer. This is a referenced pointer to keep the thread
// from evaporating out from under us.
//
Status = PsLookupProcessThreadByCid( &CapturedMessage.ClientId,
NULL,
&ClientThread );
if (!NT_SUCCESS( Status )) {
ObDereferenceObject( PortObject );
return Status;
}
//
// Acquire the mutex that guards the LpcReplyMessage field of
// the thread and get the pointer to the message that the thread
// is waiting for a reply to.
//
LpcpAcquireLpcpLock();
//
// See if the thread is waiting for a reply to the message
// specified on this call. If not then a bogus message
// has been specified, so release the mutex, dereference the thread
// and return failure.
//
if ( (ClientThread->LpcReplyMessageId != CapturedMessage.MessageId) ||
!LpcpValidateClientPort(ClientThread, PortObject, LPCP_VALIDATE_REASON_WRONG_DATA) ) {
Status = STATUS_REPLY_MESSAGE_MISMATCH;
} else {
Status = STATUS_INVALID_PARAMETER;
Msg = LpcpFindDataInfoMessage( PortObject,
CapturedMessage.MessageId,
CapturedMessage.CallbackId );
if (Msg != NULL) {
DataInfo = (PPORT_DATA_INFORMATION)((PUCHAR)&Msg->Request +
Msg->Request.u2.s2.DataInfoOffset);
//
// Make sure the caller isn't asking for an index beyond what's
// in the message
//
if (DataInfo->CountDataEntries > DataEntryIndex) {
DataEntry = &DataInfo->DataEntries[ DataEntryIndex ];
CapturedDataEntry = *DataEntry;
if (CapturedDataEntry.Size >= BufferSize) {
Status = STATUS_SUCCESS;
}
}
}
}
//
// Release the mutex that guards the LpcReplyMessage field
//
LpcpReleaseLpcpLock();
if (!NT_SUCCESS( Status )) {
ObDereferenceObject( ClientThread );
ObDereferenceObject( PortObject );
return Status;
}
//
// Copy the message data
//
if (WriteToMessageData) {
Status = MmCopyVirtualMemory( PsGetCurrentProcess(),
Buffer,
THREAD_TO_PROCESS( ClientThread ),
CapturedDataEntry.Base,
BufferSize,
PreviousMode,
&BytesCopied );
} else {
Status = MmCopyVirtualMemory( THREAD_TO_PROCESS( ClientThread ),
CapturedDataEntry.Base,
PsGetCurrentProcess(),
Buffer,
BufferSize,
PreviousMode,
&BytesCopied );
}
if (ARGUMENT_PRESENT( NumberOfBytesCopied )) {
try {
*NumberOfBytesCopied = BytesCopied;
} except( EXCEPTION_EXECUTE_HANDLER ) {
NOTHING;
}
}
//
// Dereference client thread and return the system service status.
//
ObDereferenceObject( ClientThread );
ObDereferenceObject( PortObject );
return Status;
}
BOOLEAN
FASTCALL
LpcpValidateClientPort(
IN PETHREAD Thread,
IN PLPCP_PORT_OBJECT ReplyPort,
IN ULONG Reason
)
/*++
Routine Description:
This routine validates whether the reply for a request come from
an appropriate port
Arguments:
Thread - The thread waiting for a reply
ReplyPort - The port object that is replying
Return Value:
BOOLEAN - TRUE if the reply come from a valid port
Environment:
This is called holding the global LPC mutex.
--*/
{
PLPCP_PORT_OBJECT PortThread;
PortThread = LpcpGetThreadPort(Thread);
//
// The thread must have a port set
//
if (PortThread == NULL) {
return FALSE;
}
//
// We only allow a port connected with the requestor.
// Also csrss is giving the server connection port to clients
// and we can have a client sending a request from a server connection port
// and csrss can reply with a server communication port
//
if ( ( ReplyPort == PortThread->ConnectionPort )
||
( ReplyPort == PortThread->ConnectedPort )
||
( ReplyPort == PortThread )
||
(
((ReplyPort->Flags & PORT_TYPE) == SERVER_COMMUNICATION_PORT)
&&
(ReplyPort->ConnectionPort == PortThread)
)
) {
return TRUE;
}
#if 0
if (LpcpEventCounts < LpcMaxEventLogs) {
PUNICODE_STRING StrReason;
POBJECT_NAME_INFORMATION ObjectNameInfo;
NTSTATUS Status;
ULONG Length;
PLPC_WORK_CONTEXT AuditItem;
if (PortThread->ConnectionPort) {
ObjectNameInfo = ExAllocatePoolWithTag(PagedPool, LPCP_PORT_NAME_MAX + sizeof (UNICODE_STRING), 'ScpL');
if (ObjectNameInfo != NULL) {
Status = ObQueryNameString( PortThread->ConnectionPort,
ObjectNameInfo,
LPCP_PORT_NAME_MAX,
&Length
);
if (NT_SUCCESS(Status)) {
//
// Audit the event. Use a worker thread to avoid burning
// up a bunch of cycles since the global mutex is held.
//
StrReason = (PUNICODE_STRING)((ULONG_PTR) ObjectNameInfo + LPCP_PORT_NAME_MAX);
switch (Reason) {
case LPCP_VALIDATE_REASON_IMPERSONATION:
RtlInitUnicodeString( StrReason, L"impersonation" );
break;
case LPCP_VALIDATE_REASON_REPLY:
RtlInitUnicodeString( StrReason, L"reply" );
break;
case LPCP_VALIDATE_REASON_WRONG_DATA:
RtlInitUnicodeString( StrReason, L"data access" );
break;
}
AuditItem = ExAllocatePoolWithTag (NonPagedPool,
sizeof(LPC_WORK_CONTEXT),
'wcpL');
if (AuditItem != NULL) {
AuditItem->Buffer = (PVOID) ObjectNameInfo;
ExInitializeWorkItem (&AuditItem->WorkItem,
LpcpAuditInvalidUse,
(PVOID) AuditItem);
ExQueueWorkItem (&AuditItem->WorkItem, DelayedWorkQueue);
LpcpEventCounts += 1;
}
else {
ExFreePool (ObjectNameInfo);
}
}
else {
ExFreePool (ObjectNameInfo);
}
}
}
#if DBG
if (LpcpStopOnReplyMismatch) {
DbgBreakPoint();
}
#endif
}
#endif
return FALSE;
}
#if 0
VOID
LpcpAuditInvalidUse (
IN PVOID Context
)
/*++
Routine Description:
This routine is the worker routine which logs security items.
Arguments:
Context - Supplies a pointer to the LPC_WORK_CONTEXT for the audit event.
Return Value:
None.
Environment:
Kernel mode, PASSIVE_LEVEL.
--*/
{
PUNICODE_STRING StrReason;
POBJECT_NAME_INFORMATION ObjectNameInfo;
PLPC_WORK_CONTEXT AuditItem;
PAGED_CODE();
AuditItem = (PLPC_WORK_CONTEXT) Context;
ObjectNameInfo = (POBJECT_NAME_INFORMATION) AuditItem->Buffer;
StrReason = (PUNICODE_STRING)((ULONG_PTR) ObjectNameInfo + LPCP_PORT_NAME_MAX);
SeAuditLPCInvalidUse (StrReason, &ObjectNameInfo->Name);
ExFreePool (ObjectNameInfo);
ExFreePool (AuditItem);
}
#endif