/*++ 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); } 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 ); } // // 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; }