xref: /reactos/ntoskrnl/lpc/port.c (revision c8d07514)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/lpc/port.c
5  * PURPOSE:         Local Procedure Call: Port Management
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 /* GLOBALS *******************************************************************/
16 
17 POBJECT_TYPE LpcPortObjectType, LpcWaitablePortObjectType;
18 ULONG LpcpMaxMessageSize;
19 PAGED_LOOKASIDE_LIST LpcpMessagesLookaside;
20 KGUARDED_MUTEX LpcpLock;
21 ULONG LpcpTraceLevel = 0;
22 ULONG LpcpNextMessageId = 1, LpcpNextCallbackId = 1;
23 
24 static GENERIC_MAPPING LpcpPortMapping =
25 {
26     READ_CONTROL | PORT_CONNECT,
27     DELETE | PORT_CONNECT,
28     0,
29     PORT_ALL_ACCESS
30 };
31 
32 /* PRIVATE FUNCTIONS *********************************************************/
33 
34 CODE_SEG("INIT")
35 BOOLEAN
36 NTAPI
37 LpcInitSystem(VOID)
38 {
39     OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
40     UNICODE_STRING Name;
41 
42     /* Setup the LPC Lock */
43     KeInitializeGuardedMutex(&LpcpLock);
44 
45     /* Create the Port Object Type */
46     RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
47     RtlInitUnicodeString(&Name, L"Port");
48     ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
49     ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(LPCP_NONPAGED_PORT_QUEUE);
50     ObjectTypeInitializer.DefaultPagedPoolCharge = FIELD_OFFSET(LPCP_PORT_OBJECT, WaitEvent);
51     ObjectTypeInitializer.GenericMapping = LpcpPortMapping;
52     ObjectTypeInitializer.PoolType = PagedPool;
53     ObjectTypeInitializer.UseDefaultObject = TRUE;
54     ObjectTypeInitializer.CloseProcedure = LpcpClosePort;
55     ObjectTypeInitializer.DeleteProcedure = LpcpDeletePort;
56     ObjectTypeInitializer.ValidAccessMask = PORT_ALL_ACCESS;
57     ObjectTypeInitializer.InvalidAttributes = OBJ_VALID_ATTRIBUTES & ~OBJ_CASE_INSENSITIVE;
58     ObCreateObjectType(&Name,
59                        &ObjectTypeInitializer,
60                        NULL,
61                        &LpcPortObjectType);
62 
63     /* Create the Waitable Port Object Type */
64     RtlInitUnicodeString(&Name, L"WaitablePort");
65     ObjectTypeInitializer.PoolType = NonPagedPool;
66     ObjectTypeInitializer.DefaultNonPagedPoolCharge += sizeof(LPCP_PORT_OBJECT);
67     ObjectTypeInitializer.DefaultPagedPoolCharge = 0;
68     ObjectTypeInitializer.UseDefaultObject = FALSE;
69     ObCreateObjectType(&Name,
70                        &ObjectTypeInitializer,
71                        NULL,
72                        &LpcWaitablePortObjectType);
73 
74     /* Allocate the LPC lookaside list */
75     LpcpMaxMessageSize = LPCP_MAX_MESSAGE_SIZE;
76     ExInitializePagedLookasideList(&LpcpMessagesLookaside,
77                                    NULL,
78                                    NULL,
79                                    0,
80                                    LpcpMaxMessageSize,
81                                    'McpL',
82                                    32);
83 
84     /* We're done */
85     return TRUE;
86 }
87 
88 BOOLEAN
89 NTAPI
90 LpcpValidateClientPort(
91     PETHREAD ClientThread,
92     PLPCP_PORT_OBJECT Port)
93 {
94     PLPCP_PORT_OBJECT ThreadPort;
95 
96     /* Get the thread's port */
97     ThreadPort = LpcpGetPortFromThread(ClientThread);
98     if (ThreadPort == NULL)
99     {
100         return FALSE;
101     }
102 
103     /* Check if the port matches directly */
104     if ((Port == ThreadPort) ||
105         (Port == ThreadPort->ConnectionPort) ||
106         (Port == ThreadPort->ConnectedPort))
107     {
108         return TRUE;
109     }
110 
111     /* Check if this is a communication port and the connection port matches */
112     if (((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_COMMUNICATION_PORT) &&
113         (Port->ConnectionPort == ThreadPort))
114     {
115         return TRUE;
116     }
117 
118     return FALSE;
119 }
120 
121 
122 /* PUBLIC FUNCTIONS **********************************************************/
123 
124 NTSTATUS
125 NTAPI
126 NtImpersonateClientOfPort(IN HANDLE PortHandle,
127                           IN PPORT_MESSAGE ClientMessage)
128 {
129     NTSTATUS Status;
130     KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
131     CLIENT_ID ClientId;
132     ULONG MessageId;
133     PLPCP_PORT_OBJECT Port = NULL, ConnectedPort = NULL;
134     PETHREAD ClientThread = NULL;
135     SECURITY_CLIENT_CONTEXT ClientContext;
136 
137     PAGED_CODE();
138 
139     /* Check if the call comes from user mode */
140     if (PreviousMode != KernelMode)
141     {
142         _SEH2_TRY
143         {
144             ProbeForRead(ClientMessage, sizeof(*ClientMessage), sizeof(PVOID));
145             ClientId  = ((volatile PORT_MESSAGE*)ClientMessage)->ClientId;
146             MessageId = ((volatile PORT_MESSAGE*)ClientMessage)->MessageId;
147         }
148         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
149         {
150             _SEH2_YIELD(return _SEH2_GetExceptionCode());
151         }
152         _SEH2_END;
153     }
154     else
155     {
156         ClientId  = ClientMessage->ClientId;
157         MessageId = ClientMessage->MessageId;
158     }
159 
160     /* Reference the port handle */
161     Status = ObReferenceObjectByHandle(PortHandle,
162                                        PORT_ALL_ACCESS,
163                                        LpcPortObjectType,
164                                        PreviousMode,
165                                        (PVOID*)&Port,
166                                        NULL);
167     if (!NT_SUCCESS(Status))
168     {
169         DPRINT1("Failed to reference port handle: 0x%ls\n", Status);
170         return Status;
171     }
172 
173     /* Make sure this is a connection port */
174     if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT)
175     {
176         /* It isn't, fail */
177         DPRINT1("Port is not a communication port\n");
178         Status = STATUS_INVALID_PORT_HANDLE;
179         goto Cleanup;
180     }
181 
182     /* Look up the client thread */
183     Status = PsLookupProcessThreadByCid(&ClientId, NULL, &ClientThread);
184     if (!NT_SUCCESS(Status))
185     {
186         DPRINT1("Failed to lookup client thread: 0x%ls\n", Status);
187         goto Cleanup;
188     }
189 
190     /* Acquire the lock */
191     KeAcquireGuardedMutex(&LpcpLock);
192 
193     /* Get the connected port and try to reference it */
194     ConnectedPort = Port->ConnectedPort;
195     if ((ConnectedPort == NULL) || !ObReferenceObjectSafe(ConnectedPort))
196     {
197         DPRINT1("Failed to reference the connected port\n");
198         ConnectedPort = NULL;
199         Status = STATUS_PORT_DISCONNECTED;
200         goto CleanupWithLock;
201     }
202 
203     /* Check for no-impersonation flag */
204     if ((ULONG_PTR)ClientThread->LpcReplyMessage & LPCP_THREAD_FLAG_NO_IMPERSONATION)
205     {
206         DPRINT1("Reply message has no impersonation flag set\n");
207         Status = STATUS_ACCESS_DENIED;
208         goto CleanupWithLock;
209     }
210 
211     /* Check for message id mismatch */
212     if ((ClientThread->LpcReplyMessageId != MessageId) || (MessageId == 0))
213     {
214         DPRINT1("LpcReplyMessageId mismatch: 0x%lx/0x%lx.\n",
215                 ClientThread->LpcReplyMessageId, MessageId);
216         Status = STATUS_REPLY_MESSAGE_MISMATCH;
217         goto CleanupWithLock;
218     }
219 
220     /* Validate the port */
221     if (!LpcpValidateClientPort(ClientThread, Port))
222     {
223         DPRINT1("LpcpValidateClientPort failed\n");
224         Status = STATUS_REPLY_MESSAGE_MISMATCH;
225         goto CleanupWithLock;
226     }
227 
228     /* Release the lock */
229     KeReleaseGuardedMutex(&LpcpLock);
230 
231     /* Check if security is static */
232     if (!(ConnectedPort->Flags & LPCP_SECURITY_DYNAMIC))
233     {
234         /* Use the static security for impersonation */
235         Status = SeImpersonateClientEx(&ConnectedPort->StaticSecurity, NULL);
236         goto Cleanup;
237     }
238 
239     /* Create new dynamic security */
240     Status = SeCreateClientSecurity(ClientThread,
241                                     &ConnectedPort->SecurityQos,
242                                     FALSE,
243                                     &ClientContext);
244     if (!NT_SUCCESS(Status))
245     {
246         DPRINT1("SeCreateClientSecurity failed\n");
247         goto Cleanup;
248     }
249 
250     /* Use dynamic security for impersonation */
251     Status = SeImpersonateClientEx(&ClientContext, NULL);
252 
253     /* Get rid of the security context */
254     SeDeleteClientSecurity(&ClientContext);
255 
256 Cleanup:
257 
258     if (ConnectedPort != NULL)
259         ObDereferenceObject(ConnectedPort);
260 
261     if (ClientThread != NULL)
262         ObDereferenceObject(ClientThread);
263 
264     ObDereferenceObject(Port);
265 
266     return Status;
267 
268 CleanupWithLock:
269 
270     /* Release the lock */
271     KeReleaseGuardedMutex(&LpcpLock);
272     goto Cleanup;
273 }
274 
275 NTSTATUS
276 NTAPI
277 NtQueryPortInformationProcess(VOID)
278 {
279     /* This is all this function does */
280     return STATUS_UNSUCCESSFUL;
281 }
282 
283 NTSTATUS
284 NTAPI
285 NtQueryInformationPort(IN HANDLE PortHandle,
286                        IN PORT_INFORMATION_CLASS PortInformationClass,
287                        OUT PVOID PortInformation,
288                        IN ULONG PortInformationLength,
289                        OUT PULONG ReturnLength)
290 {
291     UNIMPLEMENTED;
292     return STATUS_NOT_IMPLEMENTED;
293 }
294 
295 /* EOF */
296