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 /* It isn't, so fail */
257 ObDereferenceObject(Port
);
259 if (CapturedServerSid
!= ServerSid
)
260 SepReleaseSid(CapturedServerSid
, PreviousMode
, TRUE
);
262 return STATUS_INVALID_PORT_HANDLE
;
265 /* Check if we have a (captured) SID */
268 /* Make sure that we have a server */
269 if (Port
->ServerProcess
)
271 /* Get its token and query user information */
272 Token
= PsReferencePrimaryToken(Port
->ServerProcess
);
273 Status
= SeQueryInformationToken(Token
, TokenUser
, (PVOID
*)&TokenUserInfo
);
274 PsDereferencePrimaryToken(Token
);
276 /* Check for success */
277 if (NT_SUCCESS(Status
))
279 /* Compare the SIDs */
280 if (!RtlEqualSid(CapturedServerSid
, TokenUserInfo
->User
.Sid
))
283 Status
= STATUS_SERVER_SID_MISMATCH
;
286 /* Free token information */
287 ExFreePoolWithTag(TokenUserInfo
, TAG_SE
);
293 Status
= STATUS_SERVER_SID_MISMATCH
;
296 /* Finally release the captured SID, we don't need it anymore */
297 if (CapturedServerSid
!= ServerSid
)
298 SepReleaseSid(CapturedServerSid
, PreviousMode
, TRUE
);
300 /* Check if SID failed */
301 if (!NT_SUCCESS(Status
))
304 ObDereferenceObject(Port
);
309 /* Create the client port */
310 Status
= ObCreateObject(PreviousMode
,
315 sizeof(LPCP_PORT_OBJECT
),
318 (PVOID
*)&ClientPort
);
319 if (!NT_SUCCESS(Status
))
321 /* Failed, dereference the server port and return */
322 ObDereferenceObject(Port
);
327 * Setup the client port -- From now on, dereferencing the client port
328 * will automatically dereference the connection port too.
330 RtlZeroMemory(ClientPort
, sizeof(LPCP_PORT_OBJECT
));
331 ClientPort
->Flags
= LPCP_CLIENT_PORT
;
332 ClientPort
->ConnectionPort
= Port
;
333 ClientPort
->MaxMessageLength
= Port
->MaxMessageLength
;
334 ClientPort
->SecurityQos
= CapturedQos
;
335 InitializeListHead(&ClientPort
->LpcReplyChainHead
);
336 InitializeListHead(&ClientPort
->LpcDataInfoChainHead
);
338 /* Check if we have dynamic security */
339 if (CapturedQos
.ContextTrackingMode
== SECURITY_DYNAMIC_TRACKING
)
342 ClientPort
->Flags
|= LPCP_SECURITY_DYNAMIC
;
346 /* Create our own client security */
347 Status
= SeCreateClientSecurity(Thread
,
350 &ClientPort
->StaticSecurity
);
351 if (!NT_SUCCESS(Status
))
353 /* Security failed, dereference and return */
354 ObDereferenceObject(ClientPort
);
359 /* Initialize the port queue */
360 Status
= LpcpInitializePortQueue(ClientPort
);
361 if (!NT_SUCCESS(Status
))
364 ObDereferenceObject(ClientPort
);
368 /* Check if we have a client view */
371 /* Get the section handle */
372 Status
= ObReferenceObjectByHandle(CapturedClientView
.SectionHandle
,
377 (PVOID
*)&SectionToMap
,
379 if (!NT_SUCCESS(Status
))
382 ObDereferenceObject(ClientPort
);
386 /* Set the section offset */
387 SectionOffset
.QuadPart
= CapturedClientView
.SectionOffset
;
390 Status
= MmMapViewOfSection(SectionToMap
,
391 PsGetCurrentProcess(),
392 &ClientPort
->ClientSectionBase
,
396 &CapturedClientView
.ViewSize
,
401 /* Update the offset */
402 CapturedClientView
.SectionOffset
= SectionOffset
.LowPart
;
404 /* Check for failure */
405 if (!NT_SUCCESS(Status
))
408 ObDereferenceObject(SectionToMap
);
409 ObDereferenceObject(ClientPort
);
413 /* Update the base */
414 CapturedClientView
.ViewBase
= ClientPort
->ClientSectionBase
;
416 /* Reference and remember the process */
417 ClientPort
->MappingProcess
= PsGetCurrentProcess();
418 ObReferenceObject(ClientPort
->MappingProcess
);
426 /* Normalize connection information */
427 if (ConnectionInfoLength
> Port
->MaxConnectionInfoLength
)
429 /* Use the port's maximum allowed value */
430 ConnectionInfoLength
= Port
->MaxConnectionInfoLength
;
433 /* Allocate a message from the port zone */
434 Message
= LpcpAllocateFromPortZone();
437 /* Fail if we couldn't allocate a message */
438 if (SectionToMap
) ObDereferenceObject(SectionToMap
);
439 ObDereferenceObject(ClientPort
);
440 return STATUS_NO_MEMORY
;
443 /* Set pointer to the connection message and fill in the CID */
444 ConnectMessage
= (PLPCP_CONNECTION_MESSAGE
)(Message
+ 1);
445 Message
->Request
.ClientId
= Thread
->Cid
;
447 /* Check if we have a client view */
450 /* Set the view size */
451 Message
->Request
.ClientViewSize
= CapturedClientView
.ViewSize
;
453 /* Copy the client view and clear the server view */
454 RtlCopyMemory(&ConnectMessage
->ClientView
,
456 sizeof(CapturedClientView
));
457 RtlZeroMemory(&ConnectMessage
->ServerView
, sizeof(REMOTE_PORT_VIEW
));
461 /* Set the size to 0 and clear the connect message */
462 Message
->Request
.ClientViewSize
= 0;
463 RtlZeroMemory(ConnectMessage
, sizeof(LPCP_CONNECTION_MESSAGE
));
466 /* Set the section and client port. Port is NULL for now */
467 ConnectMessage
->ClientPort
= NULL
;
468 ConnectMessage
->SectionToMap
= SectionToMap
;
470 /* Set the data for the connection request message */
471 Message
->Request
.u1
.s1
.DataLength
= (CSHORT
)ConnectionInfoLength
+
472 sizeof(LPCP_CONNECTION_MESSAGE
);
473 Message
->Request
.u1
.s1
.TotalLength
= sizeof(LPCP_MESSAGE
) +
474 Message
->Request
.u1
.s1
.DataLength
;
475 Message
->Request
.u2
.s2
.Type
= LPC_CONNECTION_REQUEST
;
477 /* Check if we have connection information */
478 if (ConnectionInformation
)
483 RtlCopyMemory(ConnectMessage
+ 1,
484 ConnectionInformation
,
485 ConnectionInfoLength
);
487 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
489 /* Cleanup and return the exception code */
491 /* Free the message we have */
492 LpcpFreeToPortZone(Message
, 0);
494 /* Dereference other objects */
495 if (SectionToMap
) ObDereferenceObject(SectionToMap
);
496 ObDereferenceObject(ClientPort
);
499 _SEH2_YIELD(return _SEH2_GetExceptionCode());
504 /* Reset the status code */
505 Status
= STATUS_SUCCESS
;
507 /* Acquire the port lock */
508 KeAcquireGuardedMutex(&LpcpLock
);
510 /* Check if someone already deleted the port name */
511 if (Port
->Flags
& LPCP_NAME_DELETED
)
513 /* Fail the request */
514 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
518 /* Associate no thread yet */
519 Message
->RepliedToThread
= NULL
;
521 /* Generate the Message ID and set it */
522 Message
->Request
.MessageId
= LpcpNextMessageId
++;
523 if (!LpcpNextMessageId
) LpcpNextMessageId
= 1;
524 Thread
->LpcReplyMessageId
= Message
->Request
.MessageId
;
526 /* Insert the message into the queue and thread chain */
527 InsertTailList(&Port
->MsgQueue
.ReceiveHead
, &Message
->Entry
);
528 InsertTailList(&Port
->LpcReplyChainHead
, &Thread
->LpcReplyChain
);
529 Thread
->LpcReplyMessage
= Message
;
531 /* Now we can finally reference the client port and link it */
532 ObReferenceObject(ClientPort
);
533 ConnectMessage
->ClientPort
= ClientPort
;
535 /* Enter a critical region */
536 KeEnterCriticalRegion();
539 /* Add another reference to the port */
540 ObReferenceObject(Port
);
542 /* Release the lock */
543 KeReleaseGuardedMutex(&LpcpLock
);
545 /* Check for success */
546 if (NT_SUCCESS(Status
))
548 LPCTRACE(LPC_CONNECT_DEBUG
,
549 "Messages: %p/%p. Ports: %p/%p. Status: %lx\n",
556 /* If this is a waitable port, set the event */
557 if (Port
->Flags
& LPCP_WAITABLE_PORT
)
558 KeSetEvent(&Port
->WaitEvent
, 1, FALSE
);
560 /* Release the queue semaphore and leave the critical region */
561 LpcpCompleteWait(Port
->MsgQueue
.Semaphore
);
562 KeLeaveCriticalRegion();
564 /* Now wait for a reply and set 'Status' */
565 LpcpConnectWait(&Thread
->LpcReplySemaphore
, PreviousMode
);
568 /* Now, always free the connection message */
569 SectionToMap
= LpcpFreeConMsg(&Message
, &ConnectMessage
, Thread
);
571 /* Check for failure */
572 if (!NT_SUCCESS(Status
))
574 /* Check if the semaphore got signaled in the meantime */
575 if (KeReadStateSemaphore(&Thread
->LpcReplySemaphore
))
578 KeWaitForSingleObject(&Thread
->LpcReplySemaphore
,
588 /* Check if we got a message back */
591 /* Check for new return length */
592 if ((Message
->Request
.u1
.s1
.DataLength
-
593 sizeof(LPCP_CONNECTION_MESSAGE
)) < ConnectionInfoLength
)
595 /* Set new normalized connection length */
596 ConnectionInfoLength
= Message
->Request
.u1
.s1
.DataLength
-
597 sizeof(LPCP_CONNECTION_MESSAGE
);
600 /* Check if the caller had connection information */
601 if (ConnectionInformation
)
605 /* Return the connection information length if needed */
606 if (ConnectionInformationLength
)
607 *ConnectionInformationLength
= ConnectionInfoLength
;
609 /* Return the connection information */
610 RtlCopyMemory(ConnectionInformation
,
612 ConnectionInfoLength
);
614 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
616 /* Cleanup and return the exception code */
617 Status
= _SEH2_GetExceptionCode();
618 _SEH2_YIELD(goto Failure
);
623 /* Make sure we had a connected port */
624 if (ClientPort
->ConnectedPort
)
626 /* Get the message length before the port might get killed */
627 PortMessageLength
= Port
->MaxMessageLength
;
629 /* Insert the client port */
630 Status
= ObInsertObject(ClientPort
,
636 if (NT_SUCCESS(Status
))
638 LPCTRACE(LPC_CONNECT_DEBUG
,
639 "Handle: %p. Length: %lx\n",
645 /* Return the handle */
646 *PortHandle
= Handle
;
648 /* Check if maximum length was requested */
649 if (MaxMessageLength
)
650 *MaxMessageLength
= PortMessageLength
;
652 /* Check if we had a client view */
656 RtlCopyMemory(ClientView
,
657 &ConnectMessage
->ClientView
,
658 sizeof(*ClientView
));
661 /* Check if we had a server view */
665 RtlCopyMemory(ServerView
,
666 &ConnectMessage
->ServerView
,
667 sizeof(*ServerView
));
670 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
672 /* An exception happened, close the opened handle */
673 ObCloseHandle(Handle
, PreviousMode
);
674 Status
= _SEH2_GetExceptionCode();
681 /* No connection port, we failed */
682 if (SectionToMap
) ObDereferenceObject(SectionToMap
);
684 /* Acquire the lock */
685 KeAcquireGuardedMutex(&LpcpLock
);
687 /* Check if it's because the name got deleted */
688 if (!(ClientPort
->ConnectionPort
) ||
689 (Port
->Flags
& LPCP_NAME_DELETED
))
691 /* Set the correct status */
692 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
696 /* Otherwise, the caller refused us */
697 Status
= STATUS_PORT_CONNECTION_REFUSED
;
700 /* Release the lock */
701 KeReleaseGuardedMutex(&LpcpLock
);
704 ObDereferenceObject(ClientPort
);
707 /* Free the message */
708 LpcpFreeToPortZone(Message
, 0);
712 /* No reply message, fail */
713 Status
= STATUS_PORT_CONNECTION_REFUSED
;
717 ObDereferenceObject(Port
);
723 /* Check if we had a message and free it */
724 if (Message
) LpcpFreeToPortZone(Message
, 0);
726 /* Dereference other objects */
727 if (SectionToMap
) ObDereferenceObject(SectionToMap
);
728 ObDereferenceObject(ClientPort
);
729 ObDereferenceObject(Port
);
740 NtConnectPort(OUT PHANDLE PortHandle
,
741 IN PUNICODE_STRING PortName
,
742 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos
,
743 IN OUT PPORT_VIEW ClientView OPTIONAL
,
744 IN OUT PREMOTE_PORT_VIEW ServerView OPTIONAL
,
745 OUT PULONG MaxMessageLength OPTIONAL
,
746 IN OUT PVOID ConnectionInformation OPTIONAL
,
747 IN OUT PULONG ConnectionInformationLength OPTIONAL
)
749 /* Call the newer API */
750 return NtSecureConnectPort(PortHandle
,
757 ConnectionInformation
,
758 ConnectionInformationLength
);