You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1276 lines
39 KiB
1276 lines
39 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
lpcconn.c
|
|
|
|
Abstract:
|
|
|
|
Local Inter-Process Communication (LPC) connection system services.
|
|
|
|
Author:
|
|
|
|
Steve Wood (stevewo) 15-May-1989
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "lpcp.h"
|
|
|
|
//
|
|
// Local procedure prototypes
|
|
//
|
|
|
|
PVOID
|
|
LpcpFreeConMsg(
|
|
IN PLPCP_MESSAGE *Msg,
|
|
PLPCP_CONNECTION_MESSAGE *ConnectMsg,
|
|
IN PETHREAD CurrentThread
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,NtConnectPort)
|
|
#pragma alloc_text(PAGE,NtSecureConnectPort)
|
|
#pragma alloc_text(PAGE,LpcpFreeConMsg)
|
|
#endif
|
|
|
|
|
|
NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
NtConnectPort (
|
|
OUT PHANDLE PortHandle,
|
|
IN PUNICODE_STRING PortName,
|
|
IN PSECURITY_QUALITY_OF_SERVICE SecurityQos,
|
|
IN OUT PPORT_VIEW ClientView OPTIONAL,
|
|
IN OUT PREMOTE_PORT_VIEW ServerView OPTIONAL,
|
|
OUT PULONG MaxMessageLength OPTIONAL,
|
|
IN OUT PVOID ConnectionInformation OPTIONAL,
|
|
IN OUT PULONG ConnectionInformationLength OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
See NtSecureConnectPort
|
|
|
|
Arguments:
|
|
|
|
See NtSecureConnectPort
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - An appropriate status value
|
|
|
|
--*/
|
|
|
|
{
|
|
return NtSecureConnectPort( PortHandle,
|
|
PortName,
|
|
SecurityQos,
|
|
ClientView,
|
|
NULL,
|
|
ServerView,
|
|
MaxMessageLength,
|
|
ConnectionInformation,
|
|
ConnectionInformationLength );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NtSecureConnectPort (
|
|
OUT PHANDLE PortHandle,
|
|
IN PUNICODE_STRING PortName,
|
|
IN PSECURITY_QUALITY_OF_SERVICE SecurityQos,
|
|
IN OUT PPORT_VIEW ClientView OPTIONAL,
|
|
IN PSID RequiredServerSid,
|
|
IN OUT PREMOTE_PORT_VIEW ServerView OPTIONAL,
|
|
OUT PULONG MaxMessageLength OPTIONAL,
|
|
IN OUT PVOID ConnectionInformation OPTIONAL,
|
|
IN OUT PULONG ConnectionInformationLength OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
A client process can connect to a server process by name using the
|
|
NtConnectPort service.
|
|
|
|
The PortName parameter specifies the name of the server port to
|
|
connect to. It must correspond to an object name specified on a
|
|
call to NtCreatePort. The service sends a connection request to the
|
|
server thread that is listening for them with the NtListenPort
|
|
service. The client thread then blocks until a server thread
|
|
receives the connection request and responds with a call to the
|
|
NtCompleteConnectPort service. The server thread receives the ID of
|
|
the client thread, along with any information passed via the
|
|
ConnectionInformation parameter. The server thread then decides to
|
|
either accept or reject the connection request.
|
|
|
|
The server communicates the acceptance or rejection with the
|
|
NtCompleteConnectPort service. The server can pass back data to the
|
|
client about the acceptance or rejection via the
|
|
ConnectionInformation data block.
|
|
|
|
If the server accepts the connection request, then the client
|
|
receives a communication port object in the location pointed to by
|
|
the PortHandle parameter. This object handle has no name associated
|
|
with it and is private to the client process (i.e. it cannot be
|
|
inherited by a child process). The client uses the handle to send
|
|
and receive messages to/from the server process using the
|
|
NtRequestWaitReplyPort service.
|
|
|
|
If the ClientView parameter was specified, then the section handle
|
|
is examined. If it is a valid section handle, then the portion of
|
|
the section described by the SectionOffset and ViewSize fields will
|
|
be mapped into both the client and server process' address spaces.
|
|
The address in client address space will be returned in the ViewBase
|
|
field. The address in the server address space will be returned in
|
|
the ViewRemoteBase field. The actual offset and size used to map
|
|
the section will be returned in the SectionOffset and ViewSize
|
|
fields.
|
|
|
|
If the server rejects the connection request, then no communication
|
|
port object handle is returned, and the return status indicates an
|
|
error occurred. The server may optionally return information in the
|
|
ConnectionInformation data block giving the reason the connection
|
|
requests was rejected.
|
|
|
|
If the PortName does not exist, or the client process does not have
|
|
sufficient access rights then the returned status will indicate that
|
|
the port was not found.
|
|
|
|
Arguments:
|
|
|
|
PortHandle - A pointer to a variable that will receive the client
|
|
communication port object handle value.
|
|
|
|
PortName - A pointer to a port name string. The form of the name
|
|
is [\name...\name]\port_name.
|
|
|
|
SecurityQos - A pointer to security quality of service information
|
|
to be applied to the server on the client's behalf.
|
|
|
|
ClientView - An optional pointer to a structure that specifies the
|
|
section that all client threads will use to send messages to the
|
|
server.
|
|
|
|
ClientView Structure
|
|
|
|
ULONG Length - Specifies the size of this data structure in
|
|
bytes.
|
|
|
|
HANDLE SectionHandle - Specifies an open handle to a section
|
|
object.
|
|
|
|
ULONG SectionOffset - Specifies a field that will receive the
|
|
actual offset, in bytes, from the start of the section. The
|
|
initial value of this parameter specifies the byte offset
|
|
within the section that the client's view is based. The
|
|
value is rounded down to the next host page size boundary.
|
|
|
|
ULONG ViewSize - Specifies a field that will receive the
|
|
actual size, in bytes, of the view. If the value of this
|
|
parameter is zero, then the client's view of the section
|
|
will be mapped starting at the specified section offset and
|
|
continuing to the end of the section. Otherwise, the
|
|
initial value of this parameter specifies the size, in
|
|
bytes, of the client's view and is rounded up to the next
|
|
host page size boundary.
|
|
|
|
PVOID ViewBase - Specifies a field that will receive the base
|
|
address of the section in the client's address space.
|
|
|
|
PVOID ViewRemoteBase - Specifies a field that will receive
|
|
the base address of the client's section in the server's
|
|
address space. Used to generate pointers that are
|
|
meaningful to the server.
|
|
|
|
RequiredServerSid - Optionally specifies the SID that we expect the
|
|
server side of the port to possess. If not specified then we'll
|
|
connect to any server SID.
|
|
|
|
ServerView - An optional pointer to a structure that will receive
|
|
information about the server process' view in the client's
|
|
address space. The client process can use this information
|
|
to validate pointers it receives from the server process.
|
|
|
|
ServerView Structure
|
|
|
|
ULONG Length - Specifies the size of this data structure in
|
|
bytes.
|
|
|
|
PVOID ViewBase - Specifies a field that will receive the base
|
|
address of the server's section in the client's address
|
|
space.
|
|
|
|
ULONG ViewSize - Specifies a field that will receive the
|
|
size, in bytes, of the server's view in the client's address
|
|
space. If this field is zero, then server has no view in
|
|
the client's address space.
|
|
|
|
MaxMessageLength - An optional pointer to a variable that will
|
|
receive maximum length of messages that can be sent to the
|
|
server. The value of this parameter will not exceed
|
|
MAX_PORTMSG_LENGTH bytes.
|
|
|
|
ConnectionInformation - An optional pointer to uninterpreted data.
|
|
This data is intended for clients to pass package, version and
|
|
protocol identification information to the server to allow the
|
|
server to determine if it can satisify the client before
|
|
accepting the connection. Upon return to the client, the
|
|
ConnectionInformation data block contains any information passed
|
|
back from the server by its call to the NtCompleteConnectPort
|
|
service. The output data overwrites the input data.
|
|
|
|
ConnectionInformationLength - Pointer to the length of the
|
|
ConnectionInformation data block. The output value is the
|
|
length of the data stored in the ConnectionInformation data
|
|
block by the server's call to the NtCompleteConnectPort
|
|
service. This parameter is OPTIONAL only if the
|
|
ConnectionInformation parameter is null, otherwise it is
|
|
required.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - An appropriate status value.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLPCP_PORT_OBJECT ConnectionPort;
|
|
PLPCP_PORT_OBJECT ClientPort;
|
|
HANDLE Handle;
|
|
KPROCESSOR_MODE PreviousMode;
|
|
NTSTATUS Status;
|
|
ULONG ConnectionInfoLength;
|
|
PVOID SectionToMap;
|
|
PLPCP_MESSAGE Msg;
|
|
PLPCP_CONNECTION_MESSAGE ConnectMsg;
|
|
PEPROCESS CurrentProcess;
|
|
PETHREAD CurrentThread = PsGetCurrentThread();
|
|
LARGE_INTEGER SectionOffset;
|
|
PORT_VIEW CapturedClientView;
|
|
SECURITY_QUALITY_OF_SERVICE CapturedQos;
|
|
PSID CapturedRequiredServerSid;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get previous processor mode and probe input and output arguments if
|
|
// necessary.
|
|
//
|
|
|
|
PreviousMode = KeGetPreviousMode();
|
|
ConnectionInfoLength = 0;
|
|
|
|
if (PreviousMode != KernelMode) {
|
|
|
|
try {
|
|
|
|
ProbeForWriteHandle( PortHandle );
|
|
|
|
if (ARGUMENT_PRESENT( ClientView )) {
|
|
|
|
CapturedClientView = ProbeAndReadStructure( ClientView, PORT_VIEW );
|
|
|
|
if (CapturedClientView.Length != sizeof( *ClientView )) {
|
|
|
|
return( STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
ProbeForWriteSmallStructure( ClientView,
|
|
sizeof( *ClientView ),
|
|
sizeof( ULONG ));
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT( ServerView )) {
|
|
|
|
if (ProbeAndReadUlong( &ServerView->Length ) != sizeof( *ServerView )) {
|
|
|
|
return( STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
ProbeForWriteSmallStructure( ServerView,
|
|
sizeof( *ServerView ),
|
|
sizeof( ULONG ));
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT( MaxMessageLength )) {
|
|
|
|
ProbeForWriteUlong( MaxMessageLength );
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT( ConnectionInformationLength )) {
|
|
|
|
ConnectionInfoLength = ProbeAndReadUlong( ConnectionInformationLength );
|
|
ProbeForWriteUlong( ConnectionInformationLength );
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT( ConnectionInformation )) {
|
|
|
|
ProbeForWrite( ConnectionInformation,
|
|
ConnectionInfoLength,
|
|
sizeof( UCHAR ));
|
|
}
|
|
|
|
CapturedQos = ProbeAndReadStructure( SecurityQos, SECURITY_QUALITY_OF_SERVICE );
|
|
|
|
CapturedRequiredServerSid = RequiredServerSid;
|
|
|
|
if (ARGUMENT_PRESENT( RequiredServerSid )) {
|
|
|
|
Status = SeCaptureSid( RequiredServerSid,
|
|
PreviousMode,
|
|
NULL,
|
|
0,
|
|
PagedPool,
|
|
TRUE,
|
|
&CapturedRequiredServerSid );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
return( GetExceptionCode() );
|
|
}
|
|
|
|
//
|
|
// Otherwise this is a kernel mode operation
|
|
//
|
|
|
|
} else {
|
|
|
|
if (ARGUMENT_PRESENT( ClientView )) {
|
|
|
|
if (ClientView->Length != sizeof( *ClientView )) {
|
|
|
|
return( STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
CapturedClientView = *ClientView;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT( ServerView )) {
|
|
|
|
if (ServerView->Length != sizeof( *ServerView )) {
|
|
|
|
return( STATUS_INVALID_PARAMETER );
|
|
}
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT( ConnectionInformationLength )) {
|
|
|
|
ConnectionInfoLength = *ConnectionInformationLength;
|
|
}
|
|
|
|
CapturedQos = *SecurityQos;
|
|
CapturedRequiredServerSid = RequiredServerSid;
|
|
}
|
|
|
|
//
|
|
// Reference the connection port object by name. Return status if
|
|
// unsuccessful.
|
|
//
|
|
|
|
Status = ObReferenceObjectByName( PortName,
|
|
0,
|
|
NULL,
|
|
PORT_CONNECT,
|
|
LpcPortObjectType,
|
|
PreviousMode,
|
|
NULL,
|
|
(PVOID *)&ConnectionPort );
|
|
|
|
//
|
|
// If the port type object didn't work then try for a waitable port type
|
|
// object
|
|
//
|
|
|
|
if ( Status == STATUS_OBJECT_TYPE_MISMATCH ) {
|
|
|
|
Status = ObReferenceObjectByName( PortName,
|
|
0,
|
|
NULL,
|
|
PORT_CONNECT,
|
|
LpcWaitablePortObjectType,
|
|
PreviousMode,
|
|
NULL,
|
|
(PVOID *)&ConnectionPort );
|
|
}
|
|
|
|
//
|
|
// We can't locate the name so release the sid if we captured one and
|
|
// return error status back to our caller
|
|
//
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
if (CapturedRequiredServerSid != RequiredServerSid) {
|
|
|
|
SeReleaseSid( CapturedRequiredServerSid, PreviousMode, TRUE);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
LpcpTrace(("Connecting to port %wZ\n", PortName ));
|
|
|
|
//
|
|
// Error if user didn't give us a server communication port
|
|
//
|
|
|
|
if ((ConnectionPort->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
|
|
|
|
ObDereferenceObject( ConnectionPort );
|
|
|
|
if (CapturedRequiredServerSid != RequiredServerSid) {
|
|
|
|
SeReleaseSid( CapturedRequiredServerSid, PreviousMode, TRUE);
|
|
}
|
|
|
|
return STATUS_INVALID_PORT_HANDLE;
|
|
}
|
|
|
|
//
|
|
// If this is NtSecureConnectPort, validated the required SID against
|
|
// the SID of the server process. Fail if not equal.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT( RequiredServerSid )) {
|
|
|
|
PTOKEN_USER TokenInfo;
|
|
|
|
if (ConnectionPort->ServerProcess != NULL) {
|
|
|
|
PACCESS_TOKEN Token ;
|
|
|
|
Token = PsReferencePrimaryToken( ConnectionPort->ServerProcess );
|
|
|
|
|
|
Status = SeQueryInformationToken( Token,
|
|
TokenUser,
|
|
&TokenInfo );
|
|
|
|
PsDereferencePrimaryTokenEx( ConnectionPort->ServerProcess, Token );
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
if (!RtlEqualSid( CapturedRequiredServerSid, TokenInfo->User.Sid )) {
|
|
|
|
Status = STATUS_SERVER_SID_MISMATCH;
|
|
}
|
|
|
|
ExFreePool( TokenInfo );
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = STATUS_SERVER_SID_MISMATCH;
|
|
}
|
|
|
|
//
|
|
// We are all done with the required server sid if specified so
|
|
// now release one if we had to capture it
|
|
//
|
|
|
|
if (CapturedRequiredServerSid != RequiredServerSid) {
|
|
|
|
SeReleaseSid( CapturedRequiredServerSid, PreviousMode, TRUE);
|
|
}
|
|
|
|
//
|
|
// If the se information token query didn't work then return the
|
|
// error to our caller
|
|
//
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
ObDereferenceObject( ConnectionPort );
|
|
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate and initialize a client communication port object. Give
|
|
// the port a request message queue for lost reply datagrams. If
|
|
// unable to initialize the port, then deference the port object which
|
|
// will cause it to be deleted and return the system service status.
|
|
//
|
|
|
|
Status = ObCreateObject( PreviousMode,
|
|
LpcPortObjectType,
|
|
NULL,
|
|
PreviousMode,
|
|
NULL,
|
|
FIELD_OFFSET( LPCP_PORT_OBJECT, WaitEvent ),
|
|
0,
|
|
0,
|
|
(PVOID *)&ClientPort );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
ObDereferenceObject( ConnectionPort );
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Note, that from here on, none of the error paths dereference the
|
|
// connection port pointer, just the newly created client port pointer.
|
|
// The port delete routine will get called when the client port is
|
|
// deleted and it will dereference the connection port pointer stored
|
|
// in the client port object.
|
|
//
|
|
|
|
//
|
|
// Initialize the client port object to zeros and then fill in the
|
|
// fields.
|
|
//
|
|
|
|
RtlZeroMemory( ClientPort, FIELD_OFFSET( LPCP_PORT_OBJECT, WaitEvent ));
|
|
|
|
ClientPort->Flags = CLIENT_COMMUNICATION_PORT;
|
|
ClientPort->ConnectionPort = ConnectionPort;
|
|
ClientPort->MaxMessageLength = ConnectionPort->MaxMessageLength;
|
|
ClientPort->SecurityQos = CapturedQos;
|
|
|
|
InitializeListHead( &ClientPort->LpcReplyChainHead );
|
|
InitializeListHead( &ClientPort->LpcDataInfoChainHead );
|
|
|
|
//
|
|
// Set the security tracking mode, and initialize the client security
|
|
// context if it is static tracking.
|
|
//
|
|
|
|
if (CapturedQos.ContextTrackingMode == SECURITY_DYNAMIC_TRACKING) {
|
|
|
|
ClientPort->Flags |= PORT_DYNAMIC_SECURITY;
|
|
|
|
} else {
|
|
|
|
Status = SeCreateClientSecurity( CurrentThread,
|
|
&CapturedQos,
|
|
FALSE,
|
|
&ClientPort->StaticSecurity );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
ObDereferenceObject( ClientPort );
|
|
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Client communication ports get a request message queue for lost
|
|
// replies.
|
|
//
|
|
|
|
Status = LpcpInitializePortQueue( ClientPort );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
ObDereferenceObject( ClientPort );
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// If client has allocated a port memory section, then map a view of
|
|
// that section into the client's address space. Also reference the
|
|
// section object so we can pass a pointer to the section object in
|
|
// connection request message. If the server accepts the connection,
|
|
// then it will map a corresponding view of the section in the server's
|
|
// address space, using the referenced pointer passed in the connection
|
|
// request message.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT( ClientView )) {
|
|
|
|
Status = ObReferenceObjectByHandle( CapturedClientView.SectionHandle,
|
|
SECTION_MAP_READ |
|
|
SECTION_MAP_WRITE,
|
|
MmSectionObjectType,
|
|
PreviousMode,
|
|
(PVOID *)&SectionToMap,
|
|
NULL );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
ObDereferenceObject( ClientPort );
|
|
|
|
return Status;
|
|
}
|
|
|
|
SectionOffset.LowPart = CapturedClientView.SectionOffset,
|
|
SectionOffset.HighPart = 0;
|
|
|
|
CurrentProcess = PsGetCurrentProcess();
|
|
|
|
//
|
|
// Now map a view of the section using the reference we just captured
|
|
// and not the section handle itself, because the handle may have changed
|
|
//
|
|
|
|
Status = MmMapViewOfSection( SectionToMap,
|
|
CurrentProcess,
|
|
&ClientPort->ClientSectionBase,
|
|
0,
|
|
0,
|
|
&SectionOffset,
|
|
&CapturedClientView.ViewSize,
|
|
ViewUnmap,
|
|
0,
|
|
PAGE_READWRITE );
|
|
|
|
CapturedClientView.SectionOffset = SectionOffset.LowPart;
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
ObDereferenceObject( SectionToMap );
|
|
ObDereferenceObject( ClientPort );
|
|
|
|
return Status;
|
|
}
|
|
|
|
CapturedClientView.ViewBase = ClientPort->ClientSectionBase;
|
|
|
|
//
|
|
// We'll add an extra-reference to the current process, when we have
|
|
// a section mapped in that process.
|
|
//
|
|
|
|
ClientPort->MappingProcess = CurrentProcess;
|
|
|
|
ObReferenceObject( ClientPort->MappingProcess );
|
|
|
|
} else {
|
|
|
|
SectionToMap = NULL;
|
|
}
|
|
|
|
//
|
|
// Adjust the size of the connection info length that the client supplied
|
|
// to be the no longer than one the connection port will accept
|
|
//
|
|
|
|
if (ConnectionInfoLength > ConnectionPort->MaxConnectionInfoLength) {
|
|
|
|
ConnectionInfoLength = ConnectionPort->MaxConnectionInfoLength;
|
|
}
|
|
|
|
//
|
|
// At this point the client port is all setup and now we have to
|
|
// allocate a request connection message for the server and send it off
|
|
//
|
|
// Allocate a connection request message. It holds the LPCP message,
|
|
// the LPCP connection message, and the user supplied connection
|
|
// information
|
|
//
|
|
|
|
Msg = LpcpAllocateFromPortZone( sizeof( *Msg ) +
|
|
sizeof( *ConnectMsg ) +
|
|
ConnectionInfoLength );
|
|
|
|
//
|
|
// If we didn't get memory for the message then tell our caller we failed
|
|
//
|
|
|
|
if (Msg == NULL) {
|
|
|
|
if (SectionToMap != NULL) {
|
|
|
|
ObDereferenceObject( SectionToMap );
|
|
}
|
|
|
|
ObDereferenceObject( ClientPort );
|
|
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Msg points to the LPCP message, followed by ConnectMsg which points to
|
|
// the LPCP connection message, followed by client specified information.
|
|
// We'll now fill it all in.
|
|
//
|
|
|
|
ConnectMsg = (PLPCP_CONNECTION_MESSAGE)(Msg + 1);
|
|
|
|
//
|
|
// This thread originated the message
|
|
//
|
|
|
|
Msg->Request.ClientId = CurrentThread->Cid;
|
|
|
|
//
|
|
// If we have a client view then copy over the client view information
|
|
// otherwise we'll zero out all of the view information
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT( ClientView )) {
|
|
|
|
Msg->Request.ClientViewSize = CapturedClientView.ViewSize;
|
|
|
|
RtlCopyMemory( &ConnectMsg->ClientView,
|
|
&CapturedClientView,
|
|
sizeof( CapturedClientView ));
|
|
|
|
RtlZeroMemory( &ConnectMsg->ServerView, sizeof( ConnectMsg->ServerView ));
|
|
|
|
} else {
|
|
|
|
Msg->Request.ClientViewSize = 0;
|
|
RtlZeroMemory( ConnectMsg, sizeof( *ConnectMsg ));
|
|
}
|
|
|
|
ConnectMsg->ClientPort = NULL; // Set below
|
|
ConnectMsg->SectionToMap = SectionToMap;
|
|
|
|
//
|
|
// The data length is everything after the port message within the lpcp
|
|
// message. In other words the connection message and the user supplied
|
|
// information
|
|
//
|
|
|
|
Msg->Request.u1.s1.DataLength = (CSHORT)(sizeof( *ConnectMsg ) +
|
|
ConnectionInfoLength);
|
|
|
|
//
|
|
// The total length add on the LPCP message
|
|
//
|
|
|
|
Msg->Request.u1.s1.TotalLength = (CSHORT)(sizeof( *Msg ) +
|
|
Msg->Request.u1.s1.DataLength);
|
|
|
|
//
|
|
// This will be a connection request message
|
|
//
|
|
|
|
Msg->Request.u2.s2.Type = LPC_CONNECTION_REQUEST;
|
|
|
|
//
|
|
// If the caller supplied some connection information then copy
|
|
// that into place right now
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT( ConnectionInformation )) {
|
|
|
|
try {
|
|
|
|
RtlCopyMemory( ConnectMsg + 1,
|
|
ConnectionInformation,
|
|
ConnectionInfoLength );
|
|
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
//
|
|
// If we fail then cleanup after ourselves and return the
|
|
// error to our caller
|
|
//
|
|
|
|
LpcpFreeToPortZone( Msg, 0 );
|
|
|
|
if (SectionToMap != NULL) {
|
|
|
|
ObDereferenceObject( SectionToMap );
|
|
}
|
|
|
|
ObDereferenceObject( ClientPort );
|
|
|
|
return GetExceptionCode();
|
|
}
|
|
}
|
|
|
|
//
|
|
// The message is mostly ready to go now put it on the servers queue.
|
|
//
|
|
// Acquire the mutex that guards the LpcReplyMessage field of the
|
|
// thread. Also acquire the semaphore that guards the connection
|
|
// request message queue. Stamp the connection request message with
|
|
// a serial number, insert the message at the tail of the connection
|
|
// request message queue and remember the address of the message in
|
|
// the LpcReplyMessage field for the current thread.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
LpcpAcquireLpcpLockByThread(CurrentThread);
|
|
|
|
//
|
|
// See if the port name has been deleted from under us. If so, then
|
|
// don't queue the message and don't wait for a reply
|
|
//
|
|
|
|
if (ConnectionPort->Flags & PORT_NAME_DELETED) {
|
|
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
} else {
|
|
|
|
LpcpTrace(( "Send Connect Msg %lx to Port %wZ (%lx)\n", Msg, PortName, ConnectionPort ));
|
|
|
|
//
|
|
// Stamp the request message with a serial number, insert the message
|
|
// at the tail of the request message queue
|
|
//
|
|
|
|
Msg->RepliedToThread = NULL;
|
|
Msg->Request.MessageId = LpcpGenerateMessageId();
|
|
|
|
CurrentThread->LpcReplyMessageId = Msg->Request.MessageId;
|
|
|
|
InsertTailList( &ConnectionPort->MsgQueue.ReceiveHead, &Msg->Entry );
|
|
|
|
InsertTailList( &ConnectionPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
|
|
|
|
CurrentThread->LpcReplyMessage = Msg;
|
|
|
|
//
|
|
// Reference the port we are passing in the connect msg so if we die
|
|
// it will still be valid for the server in NtAcceptConnectPort. The
|
|
// reference will be released when the message is freed.
|
|
//
|
|
|
|
ObReferenceObject( ClientPort );
|
|
|
|
ConnectMsg->ClientPort = ClientPort;
|
|
|
|
KeEnterCriticalRegionThread (&CurrentThread->Tcb);
|
|
}
|
|
|
|
//
|
|
// Add an extra-reference to the connection port to prevent going away
|
|
// if the server closes the handle. The reference we already have is not enough
|
|
// because closing the connection port will delete the connection message from
|
|
// the queue, which will delete the client port, which will dereference the
|
|
// connection port. Therefore right after releasing the lock the connection
|
|
// port might be invalid in the absence of this extra reference.
|
|
//
|
|
|
|
ObReferenceObject( ConnectionPort );
|
|
|
|
LpcpReleaseLpcpLock();
|
|
|
|
//
|
|
// At this point the client's communication port is all set up and the
|
|
// connection request message is in the server's queue. So now we have
|
|
// to single the server and wait for a reply
|
|
//
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// If this is a waitable port then set the event that they might be
|
|
// waiting on
|
|
//
|
|
|
|
if ( ConnectionPort->Flags & PORT_WAITABLE ) {
|
|
|
|
KeSetEvent( &ConnectionPort->WaitEvent, 1, FALSE );
|
|
}
|
|
|
|
//
|
|
// Increment the connection request message queue semaphore by one for
|
|
// the newly inserted connection request message. Release the spin
|
|
// locks, while remaining at the dispatcher IRQL. Then wait for the
|
|
// reply to this connection request by waiting on the LpcReplySemaphore
|
|
// for the current thread.
|
|
//
|
|
|
|
KeReleaseSemaphore( ConnectionPort->MsgQueue.Semaphore,
|
|
1,
|
|
1,
|
|
FALSE );
|
|
KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
//
|
|
// A connection request is accepted if the ConnectedPort of the client's
|
|
// communication port has been filled in.
|
|
//
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
|
|
SectionToMap = LpcpFreeConMsg( &Msg, &ConnectMsg, CurrentThread );
|
|
|
|
//
|
|
// Check that we got a reply message
|
|
//
|
|
|
|
if (Msg != NULL) {
|
|
|
|
//
|
|
// Copy any connection information back to the caller, but first
|
|
// calculate the new connection data length for the reply and
|
|
// don't let it grow beyond what we probed originally
|
|
//
|
|
|
|
if ((Msg->Request.u1.s1.DataLength - sizeof( *ConnectMsg )) < ConnectionInfoLength) {
|
|
|
|
ConnectionInfoLength = Msg->Request.u1.s1.DataLength - sizeof( *ConnectMsg );
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT( ConnectionInformation )) {
|
|
|
|
try {
|
|
|
|
if (ARGUMENT_PRESENT( ConnectionInformationLength )) {
|
|
|
|
*ConnectionInformationLength = ConnectionInfoLength;
|
|
}
|
|
|
|
RtlCopyMemory( ConnectionInformation,
|
|
ConnectMsg + 1,
|
|
ConnectionInfoLength );
|
|
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
Status = GetExceptionCode();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Insert client communication port object in specified object
|
|
// table. Set port handle value if successful. If not
|
|
// successful, then the port will have been dereferenced, which
|
|
// will cause it to be freed, after our delete procedure is
|
|
// called. The delete procedure will undo the work done to
|
|
// initialize the port.
|
|
//
|
|
|
|
if (ClientPort->ConnectedPort != NULL) {
|
|
|
|
ULONG CapturedMaxMessageLength;
|
|
|
|
//
|
|
// Before we do the object insert we need to get the max
|
|
// message length because right after the call the object
|
|
// could be dereferenced and gone away
|
|
//
|
|
|
|
CapturedMaxMessageLength = ConnectionPort->MaxMessageLength;
|
|
|
|
//
|
|
// Now create a handle for the new client port object.
|
|
//
|
|
|
|
Status = ObInsertObject( ClientPort,
|
|
NULL,
|
|
PORT_ALL_ACCESS,
|
|
0,
|
|
(PVOID *)NULL,
|
|
&Handle );
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
//
|
|
// This is the only successful path through this routine.
|
|
// Set the output variables, later we'll free the msg
|
|
// back to the port zone and return to our caller
|
|
//
|
|
|
|
try {
|
|
|
|
*PortHandle = Handle;
|
|
|
|
if (ARGUMENT_PRESENT( MaxMessageLength )) {
|
|
|
|
*MaxMessageLength = CapturedMaxMessageLength;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT( ClientView )) {
|
|
|
|
RtlCopyMemory( ClientView,
|
|
&ConnectMsg->ClientView,
|
|
sizeof( *ClientView ));
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT( ServerView )) {
|
|
|
|
RtlCopyMemory( ServerView,
|
|
&ConnectMsg->ServerView,
|
|
sizeof( *ServerView ));
|
|
}
|
|
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
Status = GetExceptionCode();
|
|
NtClose( Handle );
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Otherwise we did not get a connect port from the server so
|
|
// the connection was refused
|
|
//
|
|
|
|
LpcpTrace(( "Connection request refused.\n" ));
|
|
|
|
if ( SectionToMap != NULL ) {
|
|
|
|
ObDereferenceObject( SectionToMap );
|
|
}
|
|
|
|
//
|
|
// Synchronize with the deletion path for the port object
|
|
// If the server accepted the connection and immediately
|
|
// closed the server handle, the ConnectionPort field will be NULL.
|
|
// If the server closed the connection port as well, the captured
|
|
// value for the connection port will be invalid.
|
|
//
|
|
|
|
LpcpAcquireLpcpLockByThread(CurrentThread);
|
|
|
|
if ((ClientPort->ConnectionPort == NULL)
|
|
||
|
|
(ConnectionPort->Flags & PORT_NAME_DELETED)) {
|
|
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
} else {
|
|
|
|
Status = STATUS_PORT_CONNECTION_REFUSED;
|
|
}
|
|
|
|
LpcpReleaseLpcpLock();
|
|
|
|
ObDereferenceObject( ClientPort );
|
|
}
|
|
|
|
//
|
|
// Free the reply message back to the port zone
|
|
//
|
|
|
|
LpcpFreeToPortZone( Msg, 0 );
|
|
|
|
} else {
|
|
|
|
//
|
|
// We did not get a reply message so the connection must have
|
|
// been refused
|
|
//
|
|
|
|
if (SectionToMap != NULL) {
|
|
|
|
ObDereferenceObject( SectionToMap );
|
|
}
|
|
|
|
ObDereferenceObject( ClientPort );
|
|
|
|
Status = STATUS_PORT_CONNECTION_REFUSED;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Our wait was not successful
|
|
//
|
|
|
|
//
|
|
// Remove the connection request message from the received
|
|
// queue and free the message back to the connection
|
|
// port's zone.
|
|
//
|
|
|
|
SectionToMap = LpcpFreeConMsg( &Msg, &ConnectMsg, CurrentThread );
|
|
|
|
//
|
|
// The wait was not successful, but in the meantime the server could
|
|
// replied, so it signaled the lpc semaphore. We have to clear the
|
|
// semaphore state right now.
|
|
//
|
|
|
|
if (KeReadStateSemaphore( &CurrentThread->LpcReplySemaphore )) {
|
|
|
|
KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
|
|
WrExecutive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
}
|
|
|
|
if (Msg != NULL) {
|
|
|
|
LpcpFreeToPortZone( Msg, 0 );
|
|
}
|
|
|
|
//
|
|
// If a client section was specified, then dereference the section
|
|
// object.
|
|
//
|
|
|
|
if ( SectionToMap != NULL ) {
|
|
|
|
ObDereferenceObject( SectionToMap );
|
|
}
|
|
|
|
//
|
|
// If the connection was rejected or the wait failed, then
|
|
// dereference the client port object, which will cause it to
|
|
// be deleted.
|
|
//
|
|
|
|
ObDereferenceObject( ClientPort );
|
|
}
|
|
|
|
//
|
|
// Remove the extra reference we added to the connection port
|
|
//
|
|
|
|
ObDereferenceObject( ConnectionPort );
|
|
|
|
//
|
|
// And return to our caller
|
|
//
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
PVOID
|
|
LpcpFreeConMsg (
|
|
IN PLPCP_MESSAGE *Msg,
|
|
PLPCP_CONNECTION_MESSAGE *ConnectMsg,
|
|
IN PETHREAD CurrentThread
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns a connection reply message for the specified thread
|
|
|
|
Arguments:
|
|
|
|
Msg - Receives a pointer to the LPCP message if there is a reply
|
|
|
|
ConnectMsg - Receives a pointer to the LPCP connection message if there
|
|
is a reply
|
|
|
|
CurrentThread - Specifies the thread we're to be examining
|
|
|
|
Return Value:
|
|
|
|
PVOID - Returns a pointer to the section to map in the connection message
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID SectionToMap;
|
|
PLPCP_MESSAGE LpcMessage;
|
|
|
|
//
|
|
// Acquire the LPC mutex, remove the connection request message
|
|
// from the received queue and free the message back to the connection
|
|
// port's zone.
|
|
//
|
|
|
|
LpcpAcquireLpcpLock();
|
|
|
|
//
|
|
// Remove the thread from the reply rundown list in case we did not wakeup due to
|
|
// a reply
|
|
//
|
|
|
|
if (!IsListEmpty( &CurrentThread->LpcReplyChain )) {
|
|
|
|
RemoveEntryList( &CurrentThread->LpcReplyChain );
|
|
|
|
InitializeListHead( &CurrentThread->LpcReplyChain );
|
|
}
|
|
|
|
//
|
|
// Check if the thread has an LPC reply message waiting to be handled
|
|
//
|
|
|
|
LpcMessage = LpcpGetThreadMessage(CurrentThread);
|
|
|
|
if (LpcMessage != NULL) {
|
|
|
|
//
|
|
// Take the message off the threads list
|
|
//
|
|
|
|
*Msg = LpcMessage;
|
|
|
|
if (!IsListEmpty( &LpcMessage->Entry )) {
|
|
|
|
RemoveEntryList( &LpcMessage->Entry );
|
|
InitializeListHead( &LpcMessage->Entry );
|
|
}
|
|
|
|
CurrentThread->LpcReplyMessage = NULL;
|
|
|
|
CurrentThread->LpcReplyMessageId = 0;
|
|
|
|
//
|
|
// Set the connection message pointer, and copy over the section
|
|
// to map location before zeroing it out
|
|
//
|
|
|
|
*ConnectMsg = (PLPCP_CONNECTION_MESSAGE)(LpcMessage + 1);
|
|
|
|
SectionToMap = (*ConnectMsg)->SectionToMap;
|
|
(*ConnectMsg)->SectionToMap = NULL;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Otherwise there is no LPC message to be handle so we'll return
|
|
// null's to our caller
|
|
//
|
|
|
|
*Msg = NULL;
|
|
SectionToMap = NULL;
|
|
}
|
|
|
|
//
|
|
// Release the global lock and return to our caller
|
|
//
|
|
|
|
LpcpReleaseLpcpLock();
|
|
|
|
return SectionToMap;
|
|
}
|