mirror of
https://github.com/reactos/reactos.git
synced 2024-11-18 13:01:40 +00:00
5c7ce4475e
- Change INIT_FUNCTION and INIT_SECTION to CODE_SEG("INIT") and DATA_SEG("INIT") respectively - Remove INIT_FUNCTION from function prototypes - Remove alloc_text pragma calls as they are not needed anymore
295 lines
8.6 KiB
C
295 lines
8.6 KiB
C
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: ntoskrnl/lpc/port.c
|
|
* PURPOSE: Local Procedure Call: Port Management
|
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* GLOBALS *******************************************************************/
|
|
|
|
POBJECT_TYPE LpcPortObjectType, LpcWaitablePortObjectType;
|
|
ULONG LpcpMaxMessageSize;
|
|
PAGED_LOOKASIDE_LIST LpcpMessagesLookaside;
|
|
KGUARDED_MUTEX LpcpLock;
|
|
ULONG LpcpTraceLevel = 0;
|
|
ULONG LpcpNextMessageId = 1, LpcpNextCallbackId = 1;
|
|
|
|
static GENERIC_MAPPING LpcpPortMapping =
|
|
{
|
|
READ_CONTROL | PORT_CONNECT,
|
|
DELETE | PORT_CONNECT,
|
|
0,
|
|
PORT_ALL_ACCESS
|
|
};
|
|
|
|
/* PRIVATE FUNCTIONS *********************************************************/
|
|
|
|
CODE_SEG("INIT")
|
|
BOOLEAN
|
|
NTAPI
|
|
LpcInitSystem(VOID)
|
|
{
|
|
OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
|
|
UNICODE_STRING Name;
|
|
|
|
/* Setup the LPC Lock */
|
|
KeInitializeGuardedMutex(&LpcpLock);
|
|
|
|
/* Create the Port Object Type */
|
|
RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
|
|
RtlInitUnicodeString(&Name, L"Port");
|
|
ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
|
|
ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(LPCP_NONPAGED_PORT_QUEUE);
|
|
ObjectTypeInitializer.DefaultPagedPoolCharge = FIELD_OFFSET(LPCP_PORT_OBJECT, WaitEvent);
|
|
ObjectTypeInitializer.GenericMapping = LpcpPortMapping;
|
|
ObjectTypeInitializer.PoolType = PagedPool;
|
|
ObjectTypeInitializer.UseDefaultObject = TRUE;
|
|
ObjectTypeInitializer.CloseProcedure = LpcpClosePort;
|
|
ObjectTypeInitializer.DeleteProcedure = LpcpDeletePort;
|
|
ObjectTypeInitializer.ValidAccessMask = PORT_ALL_ACCESS;
|
|
ObjectTypeInitializer.InvalidAttributes = OBJ_VALID_ATTRIBUTES & ~OBJ_CASE_INSENSITIVE;
|
|
ObCreateObjectType(&Name,
|
|
&ObjectTypeInitializer,
|
|
NULL,
|
|
&LpcPortObjectType);
|
|
|
|
/* Create the Waitable Port Object Type */
|
|
RtlInitUnicodeString(&Name, L"WaitablePort");
|
|
ObjectTypeInitializer.PoolType = NonPagedPool;
|
|
ObjectTypeInitializer.DefaultNonPagedPoolCharge += sizeof(LPCP_PORT_OBJECT);
|
|
ObjectTypeInitializer.DefaultPagedPoolCharge = 0;
|
|
ObjectTypeInitializer.UseDefaultObject = FALSE;
|
|
ObCreateObjectType(&Name,
|
|
&ObjectTypeInitializer,
|
|
NULL,
|
|
&LpcWaitablePortObjectType);
|
|
|
|
/* Allocate the LPC lookaside list */
|
|
LpcpMaxMessageSize = LPCP_MAX_MESSAGE_SIZE;
|
|
ExInitializePagedLookasideList(&LpcpMessagesLookaside,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
LpcpMaxMessageSize,
|
|
'McpL',
|
|
32);
|
|
|
|
/* We're done */
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
LpcpValidateClientPort(
|
|
PETHREAD ClientThread,
|
|
PLPCP_PORT_OBJECT Port)
|
|
{
|
|
PLPCP_PORT_OBJECT ThreadPort;
|
|
|
|
/* Get the thread's port */
|
|
ThreadPort = LpcpGetPortFromThread(ClientThread);
|
|
if (ThreadPort == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* Check if the port matches directly */
|
|
if ((Port == ThreadPort) ||
|
|
(Port == ThreadPort->ConnectionPort) ||
|
|
(Port == ThreadPort->ConnectedPort))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/* Check if this is a communication port and the connection port matches */
|
|
if (((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_COMMUNICATION_PORT) &&
|
|
(Port->ConnectionPort == ThreadPort))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/* PUBLIC FUNCTIONS **********************************************************/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtImpersonateClientOfPort(IN HANDLE PortHandle,
|
|
IN PPORT_MESSAGE ClientMessage)
|
|
{
|
|
NTSTATUS Status;
|
|
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
|
|
CLIENT_ID ClientId;
|
|
ULONG MessageId;
|
|
PLPCP_PORT_OBJECT Port = NULL, ConnectedPort = NULL;
|
|
PETHREAD ClientThread = NULL;
|
|
SECURITY_CLIENT_CONTEXT ClientContext;
|
|
|
|
PAGED_CODE();
|
|
|
|
/* Check if the call comes from user mode */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForRead(ClientMessage, sizeof(*ClientMessage), sizeof(PVOID));
|
|
ClientId = ((volatile PORT_MESSAGE*)ClientMessage)->ClientId;
|
|
MessageId = ((volatile PORT_MESSAGE*)ClientMessage)->MessageId;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else
|
|
{
|
|
ClientId = ClientMessage->ClientId;
|
|
MessageId = ClientMessage->MessageId;
|
|
}
|
|
|
|
/* Reference the port handle */
|
|
Status = ObReferenceObjectByHandle(PortHandle,
|
|
PORT_ALL_ACCESS,
|
|
LpcPortObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&Port,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to reference port handle: 0x%ls\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Make sure this is a connection port */
|
|
if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT)
|
|
{
|
|
/* It isn't, fail */
|
|
DPRINT1("Port is not a communication port\n");
|
|
Status = STATUS_INVALID_PORT_HANDLE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Look up the client thread */
|
|
Status = PsLookupProcessThreadByCid(&ClientId, NULL, &ClientThread);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to lookup client thread: 0x%ls\n", Status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Acquire the lock */
|
|
KeAcquireGuardedMutex(&LpcpLock);
|
|
|
|
/* Get the connected port and try to reference it */
|
|
ConnectedPort = Port->ConnectedPort;
|
|
if ((ConnectedPort == NULL) || !ObReferenceObjectSafe(ConnectedPort))
|
|
{
|
|
DPRINT1("Failed to reference the connected port\n");
|
|
ConnectedPort = NULL;
|
|
Status = STATUS_PORT_DISCONNECTED;
|
|
goto CleanupWithLock;
|
|
}
|
|
|
|
/* Check for no-impersonation flag */
|
|
if ((ULONG_PTR)ClientThread->LpcReplyMessage & LPCP_THREAD_FLAG_NO_IMPERSONATION)
|
|
{
|
|
DPRINT1("Reply message has no impersonation flag set\n");
|
|
Status = STATUS_ACCESS_DENIED;
|
|
goto CleanupWithLock;
|
|
}
|
|
|
|
/* Check for message id mismatch */
|
|
if ((ClientThread->LpcReplyMessageId != MessageId) || (MessageId == 0))
|
|
{
|
|
DPRINT1("LpcReplyMessageId mismatch: 0x%lx/0x%lx.\n",
|
|
ClientThread->LpcReplyMessageId, MessageId);
|
|
Status = STATUS_REPLY_MESSAGE_MISMATCH;
|
|
goto CleanupWithLock;
|
|
}
|
|
|
|
/* Validate the port */
|
|
if (!LpcpValidateClientPort(ClientThread, Port))
|
|
{
|
|
DPRINT1("LpcpValidateClientPort failed\n");
|
|
Status = STATUS_REPLY_MESSAGE_MISMATCH;
|
|
goto CleanupWithLock;
|
|
}
|
|
|
|
/* Release the lock */
|
|
KeReleaseGuardedMutex(&LpcpLock);
|
|
|
|
/* Check if security is static */
|
|
if (!(ConnectedPort->Flags & LPCP_SECURITY_DYNAMIC))
|
|
{
|
|
/* Use the static security for impersonation */
|
|
Status = SeImpersonateClientEx(&ConnectedPort->StaticSecurity, NULL);
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Create new dynamic security */
|
|
Status = SeCreateClientSecurity(ClientThread,
|
|
&ConnectedPort->SecurityQos,
|
|
FALSE,
|
|
&ClientContext);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("SeCreateClientSecurity failed\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Use dynamic security for impersonation */
|
|
Status = SeImpersonateClientEx(&ClientContext, NULL);
|
|
|
|
/* Get rid of the security context */
|
|
SeDeleteClientSecurity(&ClientContext);
|
|
|
|
Cleanup:
|
|
|
|
if (ConnectedPort != NULL)
|
|
ObDereferenceObject(ConnectedPort);
|
|
|
|
if (ClientThread != NULL)
|
|
ObDereferenceObject(ClientThread);
|
|
|
|
ObDereferenceObject(Port);
|
|
|
|
return Status;
|
|
|
|
CleanupWithLock:
|
|
|
|
/* Release the lock */
|
|
KeReleaseGuardedMutex(&LpcpLock);
|
|
goto Cleanup;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtQueryPortInformationProcess(VOID)
|
|
{
|
|
/* This is all this function does */
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtQueryInformationPort(IN HANDLE PortHandle,
|
|
IN PORT_INFORMATION_CLASS PortInformationClass,
|
|
OUT PVOID PortInformation,
|
|
IN ULONG PortInformationLength,
|
|
OUT PULONG ReturnLength)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* EOF */
|