Sync with trunk r63383 .
[reactos.git] / ntoskrnl / lpc / port.c
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 BOOLEAN
35 NTAPI
36 INIT_FUNCTION
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 RtlInitUnicodeString(&Name, L"WaitablePort");
64 ObjectTypeInitializer.PoolType = NonPagedPool;
65 ObjectTypeInitializer.DefaultNonPagedPoolCharge += sizeof(LPCP_PORT_OBJECT);
66 ObjectTypeInitializer.DefaultPagedPoolCharge = 0;
67 ObjectTypeInitializer.UseDefaultObject = FALSE;
68 ObCreateObjectType(&Name,
69 &ObjectTypeInitializer,
70 NULL,
71 &LpcWaitablePortObjectType);
72
73 /* Allocate the LPC lookaside list */
74 LpcpMaxMessageSize = LPCP_MAX_MESSAGE_SIZE;
75 ExInitializePagedLookasideList(&LpcpMessagesLookaside,
76 NULL,
77 NULL,
78 0,
79 LpcpMaxMessageSize,
80 'McpL',
81 32);
82
83 /* We're done */
84 return TRUE;
85 }
86
87 BOOLEAN
88 NTAPI
89 LpcpValidateClientPort(
90 PETHREAD ClientThread,
91 PLPCP_PORT_OBJECT Port)
92 {
93 PLPCP_PORT_OBJECT ThreadPort;
94
95 /* Get the thread's port */
96 ThreadPort = LpcpGetPortFromThread(ClientThread);
97 if (ThreadPort == NULL)
98 {
99 return FALSE;
100 }
101
102 /* Check if the port matches directly */
103 if ((Port == ThreadPort) ||
104 (Port == ThreadPort->ConnectionPort) ||
105 (Port == ThreadPort->ConnectedPort))
106 {
107 return TRUE;
108 }
109
110 /* Check if this is a communication port and the connection port matches */
111 if (((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_COMMUNICATION_PORT) &&
112 (Port->ConnectionPort == ThreadPort))
113 {
114 return TRUE;
115 }
116
117 return FALSE;
118 }
119
120
121 /* PUBLIC FUNCTIONS **********************************************************/
122
123 NTSTATUS
124 NTAPI
125 NtImpersonateClientOfPort(IN HANDLE PortHandle,
126 IN PPORT_MESSAGE ClientMessage)
127 {
128 KPROCESSOR_MODE PreviousMode;
129 CLIENT_ID ClientId;
130 ULONG MessageId;
131 PLPCP_PORT_OBJECT Port = NULL, ConnectedPort = NULL;
132 PETHREAD ClientThread = NULL;
133 SECURITY_CLIENT_CONTEXT ClientContext;
134 NTSTATUS Status;
135 PAGED_CODE();
136
137 /* Check the previous mode */
138 PreviousMode = ExGetPreviousMode();
139 if (PreviousMode == KernelMode)
140 {
141 ClientId = ClientMessage->ClientId;
142 MessageId = ClientMessage->MessageId;
143 }
144 else
145 {
146 _SEH2_TRY
147 {
148 ProbeForRead(ClientMessage, sizeof(*ClientMessage), sizeof(PVOID));
149 ClientId = ClientMessage->ClientId;
150 MessageId = ClientMessage->MessageId;
151 }
152 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
153 {
154 DPRINT1("Got exception!\n");
155 return _SEH2_GetExceptionCode();
156 }
157 _SEH2_END;
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) ||
196 !ObReferenceObjectSafe(ConnectedPort))
197 {
198 DPRINT1("Failed to reference the connected port\n");
199 ConnectedPort = NULL;
200 Status = STATUS_PORT_DISCONNECTED;
201 goto CleanupWithLock;
202 }
203
204 /* Check for no-impersonation flag */
205 if ((ULONG_PTR)ClientThread->LpcReplyMessage & LPCP_THREAD_FLAG_NO_IMPERSONATION)
206 {
207 DPRINT1("Reply message has no impersonation flag set\n");
208 Status = STATUS_ACCESS_DENIED;
209 goto CleanupWithLock;
210 }
211
212 /* Check for message id mismatch */
213 if ((ClientThread->LpcReplyMessageId != MessageId) || (MessageId == 0))
214 {
215 DPRINT1("LpcReplyMessageId mismatch: 0x%lx/0x%lx.\n",
216 ClientThread->LpcReplyMessageId, MessageId);
217 Status = STATUS_REPLY_MESSAGE_MISMATCH;
218 goto CleanupWithLock;
219 }
220
221 /* Validate the port */
222 if (!LpcpValidateClientPort(ClientThread, Port))
223 {
224 DPRINT1("LpcpValidateClientPort failed\n");
225 Status = STATUS_REPLY_MESSAGE_MISMATCH;
226 goto CleanupWithLock;
227 }
228
229 /* Release the lock */
230 KeReleaseGuardedMutex(&LpcpLock);
231
232 /* Check if security is static */
233 if (!(ConnectedPort->Flags & LPCP_SECURITY_DYNAMIC))
234 {
235 /* Use the static security for impersonation */
236 Status = SeImpersonateClientEx(&ConnectedPort->StaticSecurity, NULL);
237 goto Cleanup;
238 }
239
240 /* Create new dynamic security */
241 Status = SeCreateClientSecurity(ClientThread,
242 &ConnectedPort->SecurityQos,
243 FALSE,
244 &ClientContext);
245 if (!NT_SUCCESS(Status))
246 {
247 DPRINT1("SeCreateClientSecurity failed\n");
248 goto Cleanup;
249 }
250
251 /* Use dynamic security for impersonation */
252 Status = SeImpersonateClientEx(&ClientContext, NULL);
253
254 /* Get rid of the security context */
255 SeDeleteClientSecurity(&ClientContext);
256
257 goto Cleanup;
258
259 CleanupWithLock:
260
261 /* Release the lock */
262 KeReleaseGuardedMutex(&LpcpLock);
263
264 Cleanup:
265
266 if (ConnectedPort != NULL)
267 ObDereferenceObject(ConnectedPort);
268
269 if (ClientThread != NULL)
270 ObDereferenceObject(ClientThread);
271
272 ObDereferenceObject(Port);
273
274 return Status;
275 }
276
277 NTSTATUS
278 NTAPI
279 NtQueryPortInformationProcess(VOID)
280 {
281 /* This is all this function does */
282 return STATUS_UNSUCCESSFUL;
283 }
284
285 NTSTATUS
286 NTAPI
287 NtQueryInformationPort(IN HANDLE PortHandle,
288 IN PORT_INFORMATION_CLASS PortInformationClass,
289 OUT PVOID PortInformation,
290 IN ULONG PortInformationLength,
291 OUT PULONG ReturnLength)
292 {
293 UNIMPLEMENTED;
294 return STATUS_NOT_IMPLEMENTED;
295 }
296
297 /* EOF */