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 SecurityQos
,
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 NTSTATUS Status
= STATUS_SUCCESS
;
91 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
92 PETHREAD Thread
= PsGetCurrentThread();
93 SECURITY_QUALITY_OF_SERVICE CapturedQos
;
94 PORT_VIEW CapturedClientView
;
95 PSID CapturedServerSid
;
96 ULONG ConnectionInfoLength
= 0;
97 PLPCP_PORT_OBJECT Port
, ClientPort
;
98 PLPCP_MESSAGE Message
;
99 PLPCP_CONNECTION_MESSAGE ConnectMessage
;
100 ULONG PortMessageLength
;
103 LARGE_INTEGER SectionOffset
;
105 PTOKEN_USER TokenUserInfo
;
108 LPCTRACE(LPC_CONNECT_DEBUG
,
109 "Name: %wZ. SecurityQos: %p. Views: %p/%p. Sid: %p\n",
116 /* Check if the call comes from user mode */
117 if (PreviousMode
!= KernelMode
)
119 /* Enter SEH for probing the parameters */
122 /* Probe the PortHandle */
123 ProbeForWriteHandle(PortHandle
);
125 /* Probe and capture the QoS */
126 ProbeForRead(SecurityQos
, sizeof(*SecurityQos
), sizeof(ULONG
));
127 CapturedQos
= *(volatile SECURITY_QUALITY_OF_SERVICE
*)SecurityQos
;
128 /* NOTE: Do not care about CapturedQos.Length */
130 /* The following parameters are optional */
132 /* Capture the client view */
135 ProbeForWrite(ClientView
, sizeof(*ClientView
), sizeof(ULONG
));
136 CapturedClientView
= *(volatile PORT_VIEW
*)ClientView
;
138 /* Validate the size of the client view */
139 if (CapturedClientView
.Length
!= sizeof(CapturedClientView
))
142 _SEH2_YIELD(return STATUS_INVALID_PARAMETER
);
147 /* Capture the server view */
150 ProbeForWrite(ServerView
, sizeof(*ServerView
), sizeof(ULONG
));
152 /* Validate the size of the server view */
153 if (((volatile REMOTE_PORT_VIEW
*)ServerView
)->Length
!= sizeof(*ServerView
))
156 _SEH2_YIELD(return STATUS_INVALID_PARAMETER
);
160 if (MaxMessageLength
)
161 ProbeForWriteUlong(MaxMessageLength
);
163 /* Capture connection information length */
164 if (ConnectionInformationLength
)
166 ProbeForWriteUlong(ConnectionInformationLength
);
167 ConnectionInfoLength
= *(volatile ULONG
*)ConnectionInformationLength
;
170 /* Probe the ConnectionInformation */
171 if (ConnectionInformation
)
172 ProbeForWrite(ConnectionInformation
, ConnectionInfoLength
, sizeof(ULONG
));
174 CapturedServerSid
= ServerSid
;
175 if (ServerSid
!= NULL
)
178 Status
= SepCaptureSid(ServerSid
,
183 if (!NT_SUCCESS(Status
))
185 DPRINT1("Failed to capture ServerSid!\n");
186 _SEH2_YIELD(return Status
);
190 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
192 /* There was an exception, return the exception code */
193 _SEH2_YIELD(return _SEH2_GetExceptionCode());
199 CapturedQos
= *SecurityQos
;
200 /* NOTE: Do not care about CapturedQos.Length */
202 /* The following parameters are optional */
204 /* Capture the client view */
207 /* Validate the size of the client view */
208 if (ClientView
->Length
!= sizeof(*ClientView
))
211 return STATUS_INVALID_PARAMETER
;
213 CapturedClientView
= *ClientView
;
216 /* Capture the server view */
219 /* Validate the size of the server view */
220 if (ServerView
->Length
!= sizeof(*ServerView
))
223 return STATUS_INVALID_PARAMETER
;
227 /* Capture connection information length */
228 if (ConnectionInformationLength
)
229 ConnectionInfoLength
= *ConnectionInformationLength
;
231 CapturedServerSid
= ServerSid
;
235 Status
= ObReferenceObjectByName(PortName
,
243 if (!NT_SUCCESS(Status
))
245 DPRINT1("Failed to reference port '%wZ': 0x%lx\n", PortName
, Status
);
247 if (CapturedServerSid
!= ServerSid
)
248 SepReleaseSid(CapturedServerSid
, PreviousMode
, TRUE
);
253 /* This has to be a connection port */
254 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CONNECTION_PORT
)
256 DPRINT1("Port '%wZ' is not a connection port (Flags: 0x%lx)\n", PortName
, Port
->Flags
);
258 /* It isn't, so fail */
259 ObDereferenceObject(Port
);
261 if (CapturedServerSid
!= ServerSid
)
262 SepReleaseSid(CapturedServerSid
, PreviousMode
, TRUE
);
264 return STATUS_INVALID_PORT_HANDLE
;
267 /* Check if we have a (captured) SID */
270 /* Make sure that we have a server */
271 if (Port
->ServerProcess
)
273 /* Get its token and query user information */
274 Token
= PsReferencePrimaryToken(Port
->ServerProcess
);
275 Status
= SeQueryInformationToken(Token
, TokenUser
, (PVOID
*)&TokenUserInfo
);
276 PsDereferencePrimaryToken(Token
);
278 /* Check for success */
279 if (NT_SUCCESS(Status
))
281 /* Compare the SIDs */
282 if (!RtlEqualSid(CapturedServerSid
, TokenUserInfo
->User
.Sid
))
285 DPRINT1("Port '%wZ': server SID mismatch\n", PortName
);
286 Status
= STATUS_SERVER_SID_MISMATCH
;
289 /* Free token information */
290 ExFreePoolWithTag(TokenUserInfo
, TAG_SE
);
296 DPRINT1("Port '%wZ': server SID mismatch\n", PortName
);
297 Status
= STATUS_SERVER_SID_MISMATCH
;
300 /* Finally release the captured SID, we don't need it anymore */
301 if (CapturedServerSid
!= ServerSid
)
302 SepReleaseSid(CapturedServerSid
, PreviousMode
, TRUE
);
304 /* Check if SID failed */
305 if (!NT_SUCCESS(Status
))
308 ObDereferenceObject(Port
);
313 /* Create the client port */
314 Status
= ObCreateObject(PreviousMode
,
319 sizeof(LPCP_PORT_OBJECT
),
322 (PVOID
*)&ClientPort
);
323 if (!NT_SUCCESS(Status
))
325 /* Failed, dereference the server port and return */
326 DPRINT1("Failed to create Port object: 0x%lx\n", Status
);
327 ObDereferenceObject(Port
);
332 * Setup the client port -- From now on, dereferencing the client port
333 * will automatically dereference the connection port too.
335 RtlZeroMemory(ClientPort
, sizeof(LPCP_PORT_OBJECT
));
336 ClientPort
->Flags
= LPCP_CLIENT_PORT
;
337 ClientPort
->ConnectionPort
= Port
;
338 ClientPort
->MaxMessageLength
= Port
->MaxMessageLength
;
339 ClientPort
->SecurityQos
= CapturedQos
;
340 InitializeListHead(&ClientPort
->LpcReplyChainHead
);
341 InitializeListHead(&ClientPort
->LpcDataInfoChainHead
);
343 /* Check if we have dynamic security */
344 if (CapturedQos
.ContextTrackingMode
== SECURITY_DYNAMIC_TRACKING
)
347 ClientPort
->Flags
|= LPCP_SECURITY_DYNAMIC
;
351 /* Create our own client security */
352 Status
= SeCreateClientSecurity(Thread
,
355 &ClientPort
->StaticSecurity
);
356 if (!NT_SUCCESS(Status
))
358 /* Security failed, dereference and return */
359 DPRINT1("SeCreateClientSecurity failed: 0x%lx\n", Status
);
360 ObDereferenceObject(ClientPort
);
365 /* Initialize the port queue */
366 Status
= LpcpInitializePortQueue(ClientPort
);
367 if (!NT_SUCCESS(Status
))
370 DPRINT1("LpcpInitializePortQueue failed: 0x%lx\n", Status
);
371 ObDereferenceObject(ClientPort
);
375 /* Check if we have a client view */
378 /* Get the section handle */
379 Status
= ObReferenceObjectByHandle(CapturedClientView
.SectionHandle
,
384 (PVOID
*)&SectionToMap
,
386 if (!NT_SUCCESS(Status
))
389 DPRINT1("Failed to reference port section handle: 0x%lx\n", Status
);
390 ObDereferenceObject(ClientPort
);
394 /* Set the section offset */
395 SectionOffset
.QuadPart
= CapturedClientView
.SectionOffset
;
398 Status
= MmMapViewOfSection(SectionToMap
,
399 PsGetCurrentProcess(),
400 &ClientPort
->ClientSectionBase
,
404 &CapturedClientView
.ViewSize
,
409 /* Update the offset */
410 CapturedClientView
.SectionOffset
= SectionOffset
.LowPart
;
412 /* Check for failure */
413 if (!NT_SUCCESS(Status
))
416 DPRINT1("Failed to map port section: 0x%lx\n", Status
);
417 ObDereferenceObject(SectionToMap
);
418 ObDereferenceObject(ClientPort
);
422 /* Update the base */
423 CapturedClientView
.ViewBase
= ClientPort
->ClientSectionBase
;
425 /* Reference and remember the process */
426 ClientPort
->MappingProcess
= PsGetCurrentProcess();
427 ObReferenceObject(ClientPort
->MappingProcess
);
435 /* Normalize connection information */
436 if (ConnectionInfoLength
> Port
->MaxConnectionInfoLength
)
438 /* Use the port's maximum allowed value */
439 ConnectionInfoLength
= Port
->MaxConnectionInfoLength
;
442 /* Allocate a message from the port zone */
443 Message
= LpcpAllocateFromPortZone();
446 /* Fail if we couldn't allocate a message */
447 DPRINT1("LpcpAllocateFromPortZone failed\n");
448 if (SectionToMap
) ObDereferenceObject(SectionToMap
);
449 ObDereferenceObject(ClientPort
);
450 return STATUS_NO_MEMORY
;
453 /* Set pointer to the connection message and fill in the CID */
454 ConnectMessage
= (PLPCP_CONNECTION_MESSAGE
)(Message
+ 1);
455 Message
->Request
.ClientId
= Thread
->Cid
;
457 /* Check if we have a client view */
460 /* Set the view size */
461 Message
->Request
.ClientViewSize
= CapturedClientView
.ViewSize
;
463 /* Copy the client view and clear the server view */
464 RtlCopyMemory(&ConnectMessage
->ClientView
,
466 sizeof(CapturedClientView
));
467 RtlZeroMemory(&ConnectMessage
->ServerView
, sizeof(REMOTE_PORT_VIEW
));
471 /* Set the size to 0 and clear the connect message */
472 Message
->Request
.ClientViewSize
= 0;
473 RtlZeroMemory(ConnectMessage
, sizeof(LPCP_CONNECTION_MESSAGE
));
476 /* Set the section and client port. Port is NULL for now */
477 ConnectMessage
->ClientPort
= NULL
;
478 ConnectMessage
->SectionToMap
= SectionToMap
;
480 /* Set the data for the connection request message */
481 Message
->Request
.u1
.s1
.DataLength
= (CSHORT
)ConnectionInfoLength
+
482 sizeof(LPCP_CONNECTION_MESSAGE
);
483 Message
->Request
.u1
.s1
.TotalLength
= sizeof(LPCP_MESSAGE
) +
484 Message
->Request
.u1
.s1
.DataLength
;
485 Message
->Request
.u2
.s2
.Type
= LPC_CONNECTION_REQUEST
;
487 /* Check if we have connection information */
488 if (ConnectionInformation
)
493 RtlCopyMemory(ConnectMessage
+ 1,
494 ConnectionInformation
,
495 ConnectionInfoLength
);
497 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
499 DPRINT1("Exception 0x%lx when copying connection info to user mode\n",
500 _SEH2_GetExceptionCode());
502 /* Cleanup and return the exception code */
504 /* Free the message we have */
505 LpcpFreeToPortZone(Message
, 0);
507 /* Dereference other objects */
508 if (SectionToMap
) ObDereferenceObject(SectionToMap
);
509 ObDereferenceObject(ClientPort
);
512 _SEH2_YIELD(return _SEH2_GetExceptionCode());
517 /* Reset the status code */
518 Status
= STATUS_SUCCESS
;
520 /* Acquire the port lock */
521 KeAcquireGuardedMutex(&LpcpLock
);
523 /* Check if someone already deleted the port name */
524 if (Port
->Flags
& LPCP_NAME_DELETED
)
526 /* Fail the request */
527 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
531 /* Associate no thread yet */
532 Message
->RepliedToThread
= NULL
;
534 /* Generate the Message ID and set it */
535 Message
->Request
.MessageId
= LpcpNextMessageId
++;
536 if (!LpcpNextMessageId
) LpcpNextMessageId
= 1;
537 Thread
->LpcReplyMessageId
= Message
->Request
.MessageId
;
539 /* Insert the message into the queue and thread chain */
540 InsertTailList(&Port
->MsgQueue
.ReceiveHead
, &Message
->Entry
);
541 InsertTailList(&Port
->LpcReplyChainHead
, &Thread
->LpcReplyChain
);
542 Thread
->LpcReplyMessage
= Message
;
544 /* Now we can finally reference the client port and link it */
545 ObReferenceObject(ClientPort
);
546 ConnectMessage
->ClientPort
= ClientPort
;
548 /* Enter a critical region */
549 KeEnterCriticalRegion();
552 /* Add another reference to the port */
553 ObReferenceObject(Port
);
555 /* Release the lock */
556 KeReleaseGuardedMutex(&LpcpLock
);
558 /* Check for success */
559 if (NT_SUCCESS(Status
))
561 LPCTRACE(LPC_CONNECT_DEBUG
,
562 "Messages: %p/%p. Ports: %p/%p. Status: %lx\n",
569 /* If this is a waitable port, set the event */
570 if (Port
->Flags
& LPCP_WAITABLE_PORT
)
571 KeSetEvent(&Port
->WaitEvent
, 1, FALSE
);
573 /* Release the queue semaphore and leave the critical region */
574 LpcpCompleteWait(Port
->MsgQueue
.Semaphore
);
575 KeLeaveCriticalRegion();
577 /* Now wait for a reply and set 'Status' */
578 LpcpConnectWait(&Thread
->LpcReplySemaphore
, PreviousMode
);
581 /* Now, always free the connection message */
582 SectionToMap
= LpcpFreeConMsg(&Message
, &ConnectMessage
, Thread
);
584 /* Check for failure */
585 if (!NT_SUCCESS(Status
))
587 /* Check if the semaphore got signaled in the meantime */
588 if (KeReadStateSemaphore(&Thread
->LpcReplySemaphore
))
591 KeWaitForSingleObject(&Thread
->LpcReplySemaphore
,
601 /* Check if we got a message back */
604 /* Check for new return length */
605 if ((Message
->Request
.u1
.s1
.DataLength
-
606 sizeof(LPCP_CONNECTION_MESSAGE
)) < ConnectionInfoLength
)
608 /* Set new normalized connection length */
609 ConnectionInfoLength
= Message
->Request
.u1
.s1
.DataLength
-
610 sizeof(LPCP_CONNECTION_MESSAGE
);
613 /* Check if the caller had connection information */
614 if (ConnectionInformation
)
618 /* Return the connection information length if needed */
619 if (ConnectionInformationLength
)
620 *ConnectionInformationLength
= ConnectionInfoLength
;
622 /* Return the connection information */
623 RtlCopyMemory(ConnectionInformation
,
625 ConnectionInfoLength
);
627 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
629 /* Cleanup and return the exception code */
630 Status
= _SEH2_GetExceptionCode();
631 _SEH2_YIELD(goto Failure
);
636 /* Make sure we had a connected port */
637 if (ClientPort
->ConnectedPort
)
639 /* Get the message length before the port might get killed */
640 PortMessageLength
= Port
->MaxMessageLength
;
642 /* Insert the client port */
643 Status
= ObInsertObject(ClientPort
,
649 if (NT_SUCCESS(Status
))
651 LPCTRACE(LPC_CONNECT_DEBUG
,
652 "Handle: %p. Length: %lx\n",
658 /* Return the handle */
659 *PortHandle
= Handle
;
661 /* Check if maximum length was requested */
662 if (MaxMessageLength
)
663 *MaxMessageLength
= PortMessageLength
;
665 /* Check if we had a client view */
669 RtlCopyMemory(ClientView
,
670 &ConnectMessage
->ClientView
,
671 sizeof(*ClientView
));
674 /* Check if we had a server view */
678 RtlCopyMemory(ServerView
,
679 &ConnectMessage
->ServerView
,
680 sizeof(*ServerView
));
683 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
685 /* An exception happened, close the opened handle */
686 ObCloseHandle(Handle
, PreviousMode
);
687 Status
= _SEH2_GetExceptionCode();
694 /* No connection port, we failed */
695 if (SectionToMap
) ObDereferenceObject(SectionToMap
);
697 /* Acquire the lock */
698 KeAcquireGuardedMutex(&LpcpLock
);
700 /* Check if it's because the name got deleted */
701 if (!(ClientPort
->ConnectionPort
) ||
702 (Port
->Flags
& LPCP_NAME_DELETED
))
704 /* Set the correct status */
705 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
709 /* Otherwise, the caller refused us */
710 Status
= STATUS_PORT_CONNECTION_REFUSED
;
713 /* Release the lock */
714 KeReleaseGuardedMutex(&LpcpLock
);
717 ObDereferenceObject(ClientPort
);
720 /* Free the message */
721 LpcpFreeToPortZone(Message
, 0);
725 /* No reply message, fail */
726 Status
= STATUS_PORT_CONNECTION_REFUSED
;
730 ObDereferenceObject(Port
);
736 /* Check if we had a message and free it */
737 if (Message
) LpcpFreeToPortZone(Message
, 0);
739 /* Dereference other objects */
740 if (SectionToMap
) ObDereferenceObject(SectionToMap
);
741 ObDereferenceObject(ClientPort
);
742 ObDereferenceObject(Port
);
753 NtConnectPort(OUT PHANDLE PortHandle
,
754 IN PUNICODE_STRING PortName
,
755 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos
,
756 IN OUT PPORT_VIEW ClientView OPTIONAL
,
757 IN OUT PREMOTE_PORT_VIEW ServerView OPTIONAL
,
758 OUT PULONG MaxMessageLength OPTIONAL
,
759 IN OUT PVOID ConnectionInformation OPTIONAL
,
760 IN OUT PULONG ConnectionInformationLength OPTIONAL
)
762 /* Call the newer API */
763 return NtSecureConnectPort(PortHandle
,
770 ConnectionInformation
,
771 ConnectionInformationLength
);