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
)) return Status
;
144 /* This has to be a connection port */
145 if ((Port
->Flags
& LPCP_PORT_TYPE_MASK
) != LPCP_CONNECTION_PORT
)
147 /* It isn't, so fail */
148 ObDereferenceObject(Port
);
149 return STATUS_INVALID_PORT_HANDLE
;
152 /* Check if we have a SID */
155 /* Make sure that we have a server */
156 if (Port
->ServerProcess
)
158 /* Get its token and query user information */
159 Token
= PsReferencePrimaryToken(Port
->ServerProcess
);
160 //Status = SeQueryInformationToken(Token, TokenUser, (PVOID*)&TokenUserInfo);
161 // FIXME: Need SeQueryInformationToken
162 Status
= STATUS_SUCCESS
;
163 TokenUserInfo
= ExAllocatePool(PagedPool
, sizeof(TOKEN_USER
));
164 TokenUserInfo
->User
.Sid
= ServerSid
;
165 PsDereferencePrimaryToken(Token
);
167 /* Check for success */
168 if (NT_SUCCESS(Status
))
170 /* Compare the SIDs */
171 if (!RtlEqualSid(ServerSid
, TokenUserInfo
->User
.Sid
))
174 Status
= STATUS_SERVER_SID_MISMATCH
;
177 /* Free token information */
178 ExFreePool(TokenUserInfo
);
184 Status
= STATUS_SERVER_SID_MISMATCH
;
187 /* Check if SID failed */
188 if (!NT_SUCCESS(Status
))
191 ObDereferenceObject(Port
);
196 /* Create the client port */
197 Status
= ObCreateObject(PreviousMode
,
202 sizeof(LPCP_PORT_OBJECT
),
205 (PVOID
*)&ClientPort
);
206 if (!NT_SUCCESS(Status
))
208 /* Failed, dereference the server port and return */
209 ObDereferenceObject(Port
);
213 /* Setup the client port */
214 RtlZeroMemory(ClientPort
, sizeof(LPCP_PORT_OBJECT
));
215 ClientPort
->Flags
= LPCP_CLIENT_PORT
;
216 ClientPort
->ConnectionPort
= Port
;
217 ClientPort
->MaxMessageLength
= Port
->MaxMessageLength
;
218 ClientPort
->SecurityQos
= *Qos
;
219 InitializeListHead(&ClientPort
->LpcReplyChainHead
);
220 InitializeListHead(&ClientPort
->LpcDataInfoChainHead
);
222 /* Check if we have dynamic security */
223 if (Qos
->ContextTrackingMode
== SECURITY_DYNAMIC_TRACKING
)
226 ClientPort
->Flags
|= LPCP_SECURITY_DYNAMIC
;
230 /* Create our own client security */
231 Status
= SeCreateClientSecurity(Thread
,
234 &ClientPort
->StaticSecurity
);
235 if (!NT_SUCCESS(Status
))
237 /* Security failed, dereference and return */
238 ObDereferenceObject(ClientPort
);
243 /* Initialize the port queue */
244 Status
= LpcpInitializePortQueue(ClientPort
);
245 if (!NT_SUCCESS(Status
))
248 ObDereferenceObject(ClientPort
);
252 /* Check if we have a client view */
255 /* Get the section handle */
256 Status
= ObReferenceObjectByHandle(ClientView
->SectionHandle
,
261 (PVOID
*)&SectionToMap
,
263 if (!NT_SUCCESS(Status
))
266 ObDereferenceObject(Port
);
270 /* Set the section offset */
271 SectionOffset
.QuadPart
= ClientView
->SectionOffset
;
274 Status
= MmMapViewOfSection(SectionToMap
,
275 PsGetCurrentProcess(),
276 &ClientPort
->ClientSectionBase
,
280 &ClientView
->ViewSize
,
285 /* Update the offset */
286 ClientView
->SectionOffset
= SectionOffset
.LowPart
;
288 /* Check for failure */
289 if (!NT_SUCCESS(Status
))
292 ObDereferenceObject(SectionToMap
);
293 ObDereferenceObject(Port
);
297 /* Update the base */
298 ClientView
->ViewBase
= ClientPort
->ClientSectionBase
;
300 /* Reference and remember the process */
301 ClientPort
->MappingProcess
= PsGetCurrentProcess();
302 ObReferenceObject(ClientPort
->MappingProcess
);
310 /* Normalize connection information */
311 if (ConnectionInfoLength
> Port
->MaxConnectionInfoLength
)
313 /* Use the port's maximum allowed value */
314 ConnectionInfoLength
= Port
->MaxConnectionInfoLength
;
317 /* Allocate a message from the port zone */
318 Message
= LpcpAllocateFromPortZone();
321 /* Fail if we couldn't allocate a message */
322 if (SectionToMap
) ObDereferenceObject(SectionToMap
);
323 ObDereferenceObject(ClientPort
);
324 return STATUS_NO_MEMORY
;
327 /* Set pointer to the connection message and fill in the CID */
328 ConnectMessage
= (PLPCP_CONNECTION_MESSAGE
)(Message
+ 1);
329 Message
->Request
.ClientId
= Thread
->Cid
;
331 /* Check if we have a client view */
334 /* Set the view size */
335 Message
->Request
.ClientViewSize
= ClientView
->ViewSize
;
337 /* Copy the client view and clear the server view */
338 RtlCopyMemory(&ConnectMessage
->ClientView
,
341 RtlZeroMemory(&ConnectMessage
->ServerView
, sizeof(REMOTE_PORT_VIEW
));
345 /* Set the size to 0 and clear the connect message */
346 Message
->Request
.ClientViewSize
= 0;
347 RtlZeroMemory(ConnectMessage
, sizeof(LPCP_CONNECTION_MESSAGE
));
350 /* Set the section and client port. Port is NULL for now */
351 ConnectMessage
->ClientPort
= NULL
;
352 ConnectMessage
->SectionToMap
= SectionToMap
;
354 /* Set the data for the connection request message */
355 Message
->Request
.u1
.s1
.DataLength
= (CSHORT
)ConnectionInfoLength
+
356 sizeof(LPCP_CONNECTION_MESSAGE
);
357 Message
->Request
.u1
.s1
.TotalLength
= sizeof(LPCP_MESSAGE
) +
358 Message
->Request
.u1
.s1
.DataLength
;
359 Message
->Request
.u2
.s2
.Type
= LPC_CONNECTION_REQUEST
;
361 /* Check if we have connection information */
362 if (ConnectionInformation
)
365 RtlCopyMemory(ConnectMessage
+ 1,
366 ConnectionInformation
,
367 ConnectionInfoLength
);
370 /* Acquire the port lock */
371 KeAcquireGuardedMutex(&LpcpLock
);
373 /* Check if someone already deleted the port name */
374 if (Port
->Flags
& LPCP_NAME_DELETED
)
376 /* Fail the request */
377 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
381 /* Associate no thread yet */
382 Message
->RepliedToThread
= NULL
;
384 /* Generate the Message ID and set it */
385 Message
->Request
.MessageId
= LpcpNextMessageId
++;
386 if (!LpcpNextMessageId
) LpcpNextMessageId
= 1;
387 Thread
->LpcReplyMessageId
= Message
->Request
.MessageId
;
389 /* Insert the message into the queue and thread chain */
390 InsertTailList(&Port
->MsgQueue
.ReceiveHead
, &Message
->Entry
);
391 InsertTailList(&Port
->LpcReplyChainHead
, &Thread
->LpcReplyChain
);
392 Thread
->LpcReplyMessage
= Message
;
394 /* Now we can finally reference the client port and link it*/
395 ObReferenceObject(ClientPort
);
396 ConnectMessage
->ClientPort
= ClientPort
;
398 /* Enter a critical region */
399 KeEnterCriticalRegion();
402 /* Add another reference to the port */
403 ObReferenceObject(Port
);
405 /* Release the lock */
406 KeReleaseGuardedMutex(&LpcpLock
);
408 /* Check for success */
409 if (NT_SUCCESS(Status
))
411 LPCTRACE(LPC_CONNECT_DEBUG
,
412 "Messages: %p/%p. Ports: %p/%p. Status: %lx\n",
419 /* If this is a waitable port, set the event */
420 if (Port
->Flags
& LPCP_WAITABLE_PORT
) KeSetEvent(&Port
->WaitEvent
,
424 /* Release the queue semaphore and leave the critical region */
425 LpcpCompleteWait(Port
->MsgQueue
.Semaphore
);
426 KeLeaveCriticalRegion();
428 /* Now wait for a reply */
429 LpcpConnectWait(&Thread
->LpcReplySemaphore
, PreviousMode
);
432 /* Check for failure */
433 if (!NT_SUCCESS(Status
)) goto Cleanup
;
435 /* Free the connection message */
436 SectionToMap
= LpcpFreeConMsg(&Message
, &ConnectMessage
, Thread
);
438 /* Check if we got a message back */
441 /* Check for new return length */
442 if ((Message
->Request
.u1
.s1
.DataLength
-
443 sizeof(LPCP_CONNECTION_MESSAGE
)) < ConnectionInfoLength
)
445 /* Set new normalized connection length */
446 ConnectionInfoLength
= Message
->Request
.u1
.s1
.DataLength
-
447 sizeof(LPCP_CONNECTION_MESSAGE
);
450 /* Check if we had connection information */
451 if (ConnectionInformation
)
453 /* Check if we had a length pointer */
454 if (ConnectionInformationLength
)
456 /* Return the length */
457 *ConnectionInformationLength
= ConnectionInfoLength
;
460 /* Return the connection information */
461 RtlCopyMemory(ConnectionInformation
,
463 ConnectionInfoLength
);
466 /* Make sure we had a connected port */
467 if (ClientPort
->ConnectedPort
)
469 /* Get the message length before the port might get killed */
470 PortMessageLength
= Port
->MaxMessageLength
;
472 /* Insert the client port */
473 Status
= ObInsertObject(ClientPort
,
479 if (NT_SUCCESS(Status
))
481 /* Return the handle */
482 *PortHandle
= Handle
;
483 LPCTRACE(LPC_CONNECT_DEBUG
,
484 "Handle: %p. Length: %lx\n",
488 /* Check if maximum length was requested */
489 if (MaxMessageLength
) *MaxMessageLength
= PortMessageLength
;
491 /* Check if we had a client view */
495 RtlCopyMemory(ClientView
,
496 &ConnectMessage
->ClientView
,
500 /* Check if we had a server view */
504 RtlCopyMemory(ServerView
,
505 &ConnectMessage
->ServerView
,
506 sizeof(REMOTE_PORT_VIEW
));
512 /* No connection port, we failed */
513 if (SectionToMap
) ObDereferenceObject(SectionToMap
);
515 /* Acquire the lock */
516 KeAcquireGuardedMutex(&LpcpLock
);
518 /* Check if it's because the name got deleted */
519 if (!(ClientPort
->ConnectionPort
) ||
520 (Port
->Flags
& LPCP_NAME_DELETED
))
522 /* Set the correct status */
523 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
527 /* Otherwise, the caller refused us */
528 Status
= STATUS_PORT_CONNECTION_REFUSED
;
531 /* Release the lock */
532 KeReleaseGuardedMutex(&LpcpLock
);
535 ObDereferenceObject(ClientPort
);
538 /* Free the message */
539 LpcpFreeToPortZone(Message
, 0);
543 /* No reply message, fail */
544 if (SectionToMap
) ObDereferenceObject(SectionToMap
);
545 ObDereferenceObject(ClientPort
);
546 Status
= STATUS_PORT_CONNECTION_REFUSED
;
550 ObDereferenceObject(Port
);
554 /* We failed, free the message */
555 SectionToMap
= LpcpFreeConMsg(&Message
, &ConnectMessage
, Thread
);
557 /* Check if the semaphore got signaled */
558 if (KeReadStateSemaphore(&Thread
->LpcReplySemaphore
))
561 KeWaitForSingleObject(&Thread
->LpcReplySemaphore
,
568 /* Check if we had a message and free it */
569 if (Message
) LpcpFreeToPortZone(Message
, 0);
571 /* Dereference other objects */
572 if (SectionToMap
) ObDereferenceObject(SectionToMap
);
573 ObDereferenceObject(ClientPort
);
576 ObDereferenceObject(Port
);
585 NtConnectPort(OUT PHANDLE PortHandle
,
586 IN PUNICODE_STRING PortName
,
587 IN PSECURITY_QUALITY_OF_SERVICE Qos
,
588 IN PPORT_VIEW ClientView
,
589 IN PREMOTE_PORT_VIEW ServerView
,
590 OUT PULONG MaxMessageLength
,
591 IN PVOID ConnectionInformation
,
592 OUT PULONG ConnectionInformationLength
)
594 /* Call the newer API */
595 return NtSecureConnectPort(PortHandle
,
602 ConnectionInformation
,
603 ConnectionInformationLength
);