2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/lpc/connect.c
5 * PURPOSE: Local Procedure Call: Connection Management
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
15 /* PRIVATE FUNCTIONS *********************************************************/
19 LpcpFreeConMsg(IN OUT PLPCP_MESSAGE
*Message
,
20 IN OUT PLPCP_CONNECTION_MESSAGE
*ConnectMessage
,
21 IN PETHREAD CurrentThread
)
24 PLPCP_MESSAGE ReplyMessage
;
26 /* Acquire the LPC lock */
27 KeAcquireGuardedMutex(&LpcpLock
);
29 /* Check if the reply chain is not empty */
30 if (!IsListEmpty(&CurrentThread
->LpcReplyChain
))
32 /* Remove this entry and re-initialize it */
33 RemoveEntryList(&CurrentThread
->LpcReplyChain
);
34 InitializeListHead(&CurrentThread
->LpcReplyChain
);
37 /* Check if there's a reply message */
38 ReplyMessage
= LpcpGetMessageFromThread(CurrentThread
);
42 *Message
= ReplyMessage
;
44 /* Check if it's got messages */
45 if (!IsListEmpty(&ReplyMessage
->Entry
))
48 RemoveEntryList(&ReplyMessage
->Entry
);
49 InitializeListHead(&ReplyMessage
->Entry
);
52 /* Clear message data */
53 CurrentThread
->LpcReceivedMessageId
= 0;
54 CurrentThread
->LpcReplyMessage
= NULL
;
56 /* Get the connection message and clear the section */
57 *ConnectMessage
= (PLPCP_CONNECTION_MESSAGE
)(ReplyMessage
+ 1);
58 SectionToMap
= (*ConnectMessage
)->SectionToMap
;
59 (*ConnectMessage
)->SectionToMap
= NULL
;
63 /* No message to return */
68 /* Release the lock and return the section */
69 KeReleaseGuardedMutex(&LpcpLock
);
73 /* PUBLIC FUNCTIONS **********************************************************/
80 NtSecureConnectPort(OUT PHANDLE PortHandle
,
81 IN PUNICODE_STRING PortName
,
82 IN PSECURITY_QUALITY_OF_SERVICE Qos
,
83 IN OUT PPORT_VIEW ClientView OPTIONAL
,
84 IN PSID ServerSid OPTIONAL
,
85 IN OUT PREMOTE_PORT_VIEW ServerView OPTIONAL
,
86 OUT PULONG MaxMessageLength OPTIONAL
,
87 IN OUT PVOID ConnectionInformation OPTIONAL
,
88 IN OUT PULONG ConnectionInformationLength OPTIONAL
)
90 ULONG ConnectionInfoLength
= 0;
91 PLPCP_PORT_OBJECT Port
, ClientPort
;
92 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
93 NTSTATUS Status
= STATUS_SUCCESS
;
96 PLPCP_MESSAGE Message
;
97 PLPCP_CONNECTION_MESSAGE ConnectMessage
;
98 PETHREAD Thread
= PsGetCurrentThread();
99 ULONG PortMessageLength
;
100 LARGE_INTEGER SectionOffset
;
102 PTOKEN_USER TokenUserInfo
;
104 LPCTRACE(LPC_CONNECT_DEBUG
,
105 "Name: %wZ. Qos: %p. Views: %p/%p. Sid: %p\n",
112 /* Validate client view */
113 if ((ClientView
) && (ClientView
->Length
!= sizeof(PORT_VIEW
)))
116 return STATUS_INVALID_PARAMETER
;
119 /* Validate server view */
120 if ((ServerView
) && (ServerView
->Length
!= sizeof(REMOTE_PORT_VIEW
)))
123 return STATUS_INVALID_PARAMETER
;
126 /* Check if caller sent connection information length */
127 if (ConnectionInformationLength
)
129 /* Retrieve the input length */
130 ConnectionInfoLength
= *ConnectionInformationLength
;
134 Status
= ObReferenceObjectByName(PortName
,
142 if (!NT_SUCCESS(Status
))
144 DPRINT1("Failed to reference port '%wZ': 0x%lx\n", PortName
, Status
);
148 /* This has to be a connection port */
149 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CONNECTION_PORT
)
151 /* It isn't, so fail */
152 ObDereferenceObject(Port
);
153 return STATUS_INVALID_PORT_HANDLE
;
156 /* Check if we have a SID */
159 /* Make sure that we have a server */
160 if (Port
->ServerProcess
)
162 /* Get its token and query user information */
163 Token
= PsReferencePrimaryToken(Port
->ServerProcess
);
164 //Status = SeQueryInformationToken(Token, TokenUser, (PVOID*)&TokenUserInfo);
165 // FIXME: Need SeQueryInformationToken
166 Status
= STATUS_SUCCESS
;
167 TokenUserInfo
= ExAllocatePoolWithTag(PagedPool
, sizeof(TOKEN_USER
), TAG_SE
);
168 TokenUserInfo
->User
.Sid
= ServerSid
;
169 PsDereferencePrimaryToken(Token
);
171 /* Check for success */
172 if (NT_SUCCESS(Status
))
174 /* Compare the SIDs */
175 if (!RtlEqualSid(ServerSid
, TokenUserInfo
->User
.Sid
))
178 Status
= STATUS_SERVER_SID_MISMATCH
;
181 /* Free token information */
182 ExFreePoolWithTag(TokenUserInfo
, TAG_SE
);
188 Status
= STATUS_SERVER_SID_MISMATCH
;
191 /* Check if SID failed */
192 if (!NT_SUCCESS(Status
))
195 ObDereferenceObject(Port
);
200 /* Create the client port */
201 Status
= ObCreateObject(PreviousMode
,
206 sizeof(LPCP_PORT_OBJECT
),
209 (PVOID
*)&ClientPort
);
210 if (!NT_SUCCESS(Status
))
212 /* Failed, dereference the server port and return */
213 ObDereferenceObject(Port
);
217 /* Setup the client port */
218 RtlZeroMemory(ClientPort
, sizeof(LPCP_PORT_OBJECT
));
219 ClientPort
->Flags
= LPCP_CLIENT_PORT
;
220 ClientPort
->ConnectionPort
= Port
;
221 ClientPort
->MaxMessageLength
= Port
->MaxMessageLength
;
222 ClientPort
->SecurityQos
= *Qos
;
223 InitializeListHead(&ClientPort
->LpcReplyChainHead
);
224 InitializeListHead(&ClientPort
->LpcDataInfoChainHead
);
226 /* Check if we have dynamic security */
227 if (Qos
->ContextTrackingMode
== SECURITY_DYNAMIC_TRACKING
)
230 ClientPort
->Flags
|= LPCP_SECURITY_DYNAMIC
;
234 /* Create our own client security */
235 Status
= SeCreateClientSecurity(Thread
,
238 &ClientPort
->StaticSecurity
);
239 if (!NT_SUCCESS(Status
))
241 /* Security failed, dereference and return */
242 ObDereferenceObject(ClientPort
);
247 /* Initialize the port queue */
248 Status
= LpcpInitializePortQueue(ClientPort
);
249 if (!NT_SUCCESS(Status
))
252 ObDereferenceObject(ClientPort
);
256 /* Check if we have a client view */
259 /* Get the section handle */
260 Status
= ObReferenceObjectByHandle(ClientView
->SectionHandle
,
265 (PVOID
*)&SectionToMap
,
267 if (!NT_SUCCESS(Status
))
270 ObDereferenceObject(Port
);
274 /* Set the section offset */
275 SectionOffset
.QuadPart
= ClientView
->SectionOffset
;
278 Status
= MmMapViewOfSection(SectionToMap
,
279 PsGetCurrentProcess(),
280 &ClientPort
->ClientSectionBase
,
284 &ClientView
->ViewSize
,
289 /* Update the offset */
290 ClientView
->SectionOffset
= SectionOffset
.LowPart
;
292 /* Check for failure */
293 if (!NT_SUCCESS(Status
))
296 ObDereferenceObject(SectionToMap
);
297 ObDereferenceObject(Port
);
301 /* Update the base */
302 ClientView
->ViewBase
= ClientPort
->ClientSectionBase
;
304 /* Reference and remember the process */
305 ClientPort
->MappingProcess
= PsGetCurrentProcess();
306 ObReferenceObject(ClientPort
->MappingProcess
);
314 /* Normalize connection information */
315 if (ConnectionInfoLength
> Port
->MaxConnectionInfoLength
)
317 /* Use the port's maximum allowed value */
318 ConnectionInfoLength
= Port
->MaxConnectionInfoLength
;
321 /* Allocate a message from the port zone */
322 Message
= LpcpAllocateFromPortZone();
325 /* Fail if we couldn't allocate a message */
326 if (SectionToMap
) ObDereferenceObject(SectionToMap
);
327 ObDereferenceObject(ClientPort
);
328 return STATUS_NO_MEMORY
;
331 /* Set pointer to the connection message and fill in the CID */
332 ConnectMessage
= (PLPCP_CONNECTION_MESSAGE
)(Message
+ 1);
333 Message
->Request
.ClientId
= Thread
->Cid
;
335 /* Check if we have a client view */
338 /* Set the view size */
339 Message
->Request
.ClientViewSize
= ClientView
->ViewSize
;
341 /* Copy the client view and clear the server view */
342 RtlCopyMemory(&ConnectMessage
->ClientView
,
345 RtlZeroMemory(&ConnectMessage
->ServerView
, sizeof(REMOTE_PORT_VIEW
));
349 /* Set the size to 0 and clear the connect message */
350 Message
->Request
.ClientViewSize
= 0;
351 RtlZeroMemory(ConnectMessage
, sizeof(LPCP_CONNECTION_MESSAGE
));
354 /* Set the section and client port. Port is NULL for now */
355 ConnectMessage
->ClientPort
= NULL
;
356 ConnectMessage
->SectionToMap
= SectionToMap
;
358 /* Set the data for the connection request message */
359 Message
->Request
.u1
.s1
.DataLength
= (CSHORT
)ConnectionInfoLength
+
360 sizeof(LPCP_CONNECTION_MESSAGE
);
361 Message
->Request
.u1
.s1
.TotalLength
= sizeof(LPCP_MESSAGE
) +
362 Message
->Request
.u1
.s1
.DataLength
;
363 Message
->Request
.u2
.s2
.Type
= LPC_CONNECTION_REQUEST
;
365 /* Check if we have connection information */
366 if (ConnectionInformation
)
369 RtlCopyMemory(ConnectMessage
+ 1,
370 ConnectionInformation
,
371 ConnectionInfoLength
);
374 /* Acquire the port lock */
375 KeAcquireGuardedMutex(&LpcpLock
);
377 /* Check if someone already deleted the port name */
378 if (Port
->Flags
& LPCP_NAME_DELETED
)
380 /* Fail the request */
381 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
385 /* Associate no thread yet */
386 Message
->RepliedToThread
= NULL
;
388 /* Generate the Message ID and set it */
389 Message
->Request
.MessageId
= LpcpNextMessageId
++;
390 if (!LpcpNextMessageId
) LpcpNextMessageId
= 1;
391 Thread
->LpcReplyMessageId
= Message
->Request
.MessageId
;
393 /* Insert the message into the queue and thread chain */
394 InsertTailList(&Port
->MsgQueue
.ReceiveHead
, &Message
->Entry
);
395 InsertTailList(&Port
->LpcReplyChainHead
, &Thread
->LpcReplyChain
);
396 Thread
->LpcReplyMessage
= Message
;
398 /* Now we can finally reference the client port and link it*/
399 ObReferenceObject(ClientPort
);
400 ConnectMessage
->ClientPort
= ClientPort
;
402 /* Enter a critical region */
403 KeEnterCriticalRegion();
406 /* Add another reference to the port */
407 ObReferenceObject(Port
);
409 /* Release the lock */
410 KeReleaseGuardedMutex(&LpcpLock
);
412 /* Check for success */
413 if (NT_SUCCESS(Status
))
415 LPCTRACE(LPC_CONNECT_DEBUG
,
416 "Messages: %p/%p. Ports: %p/%p. Status: %lx\n",
423 /* If this is a waitable port, set the event */
424 if (Port
->Flags
& LPCP_WAITABLE_PORT
) KeSetEvent(&Port
->WaitEvent
,
428 /* Release the queue semaphore and leave the critical region */
429 LpcpCompleteWait(Port
->MsgQueue
.Semaphore
);
430 KeLeaveCriticalRegion();
432 /* Now wait for a reply */
433 LpcpConnectWait(&Thread
->LpcReplySemaphore
, PreviousMode
);
436 /* Check for failure */
437 if (!NT_SUCCESS(Status
)) goto Cleanup
;
439 /* Free the connection message */
440 SectionToMap
= LpcpFreeConMsg(&Message
, &ConnectMessage
, Thread
);
442 /* Check if we got a message back */
445 /* Check for new return length */
446 if ((Message
->Request
.u1
.s1
.DataLength
-
447 sizeof(LPCP_CONNECTION_MESSAGE
)) < ConnectionInfoLength
)
449 /* Set new normalized connection length */
450 ConnectionInfoLength
= Message
->Request
.u1
.s1
.DataLength
-
451 sizeof(LPCP_CONNECTION_MESSAGE
);
454 /* Check if we had connection information */
455 if (ConnectionInformation
)
457 /* Check if we had a length pointer */
458 if (ConnectionInformationLength
)
460 /* Return the length */
461 *ConnectionInformationLength
= ConnectionInfoLength
;
464 /* Return the connection information */
465 RtlCopyMemory(ConnectionInformation
,
467 ConnectionInfoLength
);
470 /* Make sure we had a connected port */
471 if (ClientPort
->ConnectedPort
)
473 /* Get the message length before the port might get killed */
474 PortMessageLength
= Port
->MaxMessageLength
;
476 /* Insert the client port */
477 Status
= ObInsertObject(ClientPort
,
483 if (NT_SUCCESS(Status
))
485 /* Return the handle */
486 *PortHandle
= Handle
;
487 LPCTRACE(LPC_CONNECT_DEBUG
,
488 "Handle: %p. Length: %lx\n",
492 /* Check if maximum length was requested */
493 if (MaxMessageLength
) *MaxMessageLength
= PortMessageLength
;
495 /* Check if we had a client view */
499 RtlCopyMemory(ClientView
,
500 &ConnectMessage
->ClientView
,
504 /* Check if we had a server view */
508 RtlCopyMemory(ServerView
,
509 &ConnectMessage
->ServerView
,
510 sizeof(REMOTE_PORT_VIEW
));
516 /* No connection port, we failed */
517 if (SectionToMap
) ObDereferenceObject(SectionToMap
);
519 /* Acquire the lock */
520 KeAcquireGuardedMutex(&LpcpLock
);
522 /* Check if it's because the name got deleted */
523 if (!(ClientPort
->ConnectionPort
) ||
524 (Port
->Flags
& LPCP_NAME_DELETED
))
526 /* Set the correct status */
527 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
531 /* Otherwise, the caller refused us */
532 Status
= STATUS_PORT_CONNECTION_REFUSED
;
535 /* Release the lock */
536 KeReleaseGuardedMutex(&LpcpLock
);
539 ObDereferenceObject(ClientPort
);
542 /* Free the message */
543 LpcpFreeToPortZone(Message
, 0);
547 /* No reply message, fail */
548 if (SectionToMap
) ObDereferenceObject(SectionToMap
);
549 ObDereferenceObject(ClientPort
);
550 Status
= STATUS_PORT_CONNECTION_REFUSED
;
554 ObDereferenceObject(Port
);
558 /* We failed, free the message */
559 SectionToMap
= LpcpFreeConMsg(&Message
, &ConnectMessage
, Thread
);
561 /* Check if the semaphore got signaled */
562 if (KeReadStateSemaphore(&Thread
->LpcReplySemaphore
))
565 KeWaitForSingleObject(&Thread
->LpcReplySemaphore
,
572 /* Check if we had a message and free it */
573 if (Message
) LpcpFreeToPortZone(Message
, 0);
575 /* Dereference other objects */
576 if (SectionToMap
) ObDereferenceObject(SectionToMap
);
577 ObDereferenceObject(ClientPort
);
580 ObDereferenceObject(Port
);
589 NtConnectPort(OUT PHANDLE PortHandle
,
590 IN PUNICODE_STRING PortName
,
591 IN PSECURITY_QUALITY_OF_SERVICE Qos
,
592 IN PPORT_VIEW ClientView
,
593 IN PREMOTE_PORT_VIEW ServerView
,
594 OUT PULONG MaxMessageLength
,
595 IN PVOID ConnectionInformation
,
596 OUT PULONG ConnectionInformationLength
)
598 /* Call the newer API */
599 return NtSecureConnectPort(PortHandle
,
606 ConnectionInformation
,
607 ConnectionInformationLength
);