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
LpcInitSystem(VOID)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
LpcpValidateClientPort(PETHREAD ClientThread,PLPCP_PORT_OBJECT Port)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
NtImpersonateClientOfPort(IN HANDLE PortHandle,IN PPORT_MESSAGE ClientMessage)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
NtQueryPortInformationProcess(VOID)277 NtQueryPortInformationProcess(VOID)
278 {
279 /* This is all this function does */
280 return STATUS_UNSUCCESSFUL;
281 }
282
283 NTSTATUS
284 NTAPI
NtQueryInformationPort(IN HANDLE PortHandle,IN PORT_INFORMATION_CLASS PortInformationClass,OUT PVOID PortInformation,IN ULONG PortInformationLength,OUT PULONG ReturnLength)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