Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1260 lines
37 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. lpcconn.c
  5. Abstract:
  6. Local Inter-Process Communication (LPC) connection system services.
  7. Author:
  8. Steve Wood (stevewo) 15-May-1989
  9. Revision History:
  10. --*/
  11. #include "lpcp.h"
  12. //
  13. // Local procedure prototypes
  14. //
  15. PVOID
  16. LpcpFreeConMsg(
  17. IN PLPCP_MESSAGE *Msg,
  18. PLPCP_CONNECTION_MESSAGE *ConnectMsg,
  19. IN PETHREAD CurrentThread
  20. );
  21. #ifdef ALLOC_PRAGMA
  22. #pragma alloc_text(PAGE,NtConnectPort)
  23. #pragma alloc_text(PAGE,NtSecureConnectPort)
  24. #pragma alloc_text(PAGE,LpcpFreeConMsg)
  25. #endif
  26. NTSYSAPI
  27. NTSTATUS
  28. NTAPI
  29. NtConnectPort (
  30. OUT PHANDLE PortHandle,
  31. IN PUNICODE_STRING PortName,
  32. IN PSECURITY_QUALITY_OF_SERVICE SecurityQos,
  33. IN OUT PPORT_VIEW ClientView OPTIONAL,
  34. IN OUT PREMOTE_PORT_VIEW ServerView OPTIONAL,
  35. OUT PULONG MaxMessageLength OPTIONAL,
  36. IN OUT PVOID ConnectionInformation OPTIONAL,
  37. IN OUT PULONG ConnectionInformationLength OPTIONAL
  38. )
  39. /*++
  40. Routine Description:
  41. See NtSecureConnectPort
  42. Arguments:
  43. See NtSecureConnectPort
  44. Return Value:
  45. NTSTATUS - An appropriate status value
  46. --*/
  47. {
  48. return NtSecureConnectPort( PortHandle,
  49. PortName,
  50. SecurityQos,
  51. ClientView,
  52. NULL,
  53. ServerView,
  54. MaxMessageLength,
  55. ConnectionInformation,
  56. ConnectionInformationLength );
  57. }
  58. NTSTATUS
  59. NtSecureConnectPort (
  60. OUT PHANDLE PortHandle,
  61. IN PUNICODE_STRING PortName,
  62. IN PSECURITY_QUALITY_OF_SERVICE SecurityQos,
  63. IN OUT PPORT_VIEW ClientView OPTIONAL,
  64. IN PSID RequiredServerSid,
  65. IN OUT PREMOTE_PORT_VIEW ServerView OPTIONAL,
  66. OUT PULONG MaxMessageLength OPTIONAL,
  67. IN OUT PVOID ConnectionInformation OPTIONAL,
  68. IN OUT PULONG ConnectionInformationLength OPTIONAL
  69. )
  70. /*++
  71. Routine Description:
  72. A client process can connect to a server process by name using the
  73. NtConnectPort service.
  74. The PortName parameter specifies the name of the server port to
  75. connect to. It must correspond to an object name specified on a
  76. call to NtCreatePort. The service sends a connection request to the
  77. server thread that is listening for them with the NtListenPort
  78. service. The client thread then blocks until a server thread
  79. receives the connection request and responds with a call to the
  80. NtCompleteConnectPort service. The server thread receives the ID of
  81. the client thread, along with any information passed via the
  82. ConnectionInformation parameter. The server thread then decides to
  83. either accept or reject the connection request.
  84. The server communicates the acceptance or rejection with the
  85. NtCompleteConnectPort service. The server can pass back data to the
  86. client about the acceptance or rejection via the
  87. ConnectionInformation data block.
  88. If the server accepts the connection request, then the client
  89. receives a communication port object in the location pointed to by
  90. the PortHandle parameter. This object handle has no name associated
  91. with it and is private to the client process (i.e. it cannot be
  92. inherited by a child process). The client uses the handle to send
  93. and receive messages to/from the server process using the
  94. NtRequestWaitReplyPort service.
  95. If the ClientView parameter was specified, then the section handle
  96. is examined. If it is a valid section handle, then the portion of
  97. the section described by the SectionOffset and ViewSize fields will
  98. be mapped into both the client and server process' address spaces.
  99. The address in client address space will be returned in the ViewBase
  100. field. The address in the server address space will be returned in
  101. the ViewRemoteBase field. The actual offset and size used to map
  102. the section will be returned in the SectionOffset and ViewSize
  103. fields.
  104. If the server rejects the connection request, then no communication
  105. port object handle is returned, and the return status indicates an
  106. error occurred. The server may optionally return information in the
  107. ConnectionInformation data block giving the reason the connection
  108. requests was rejected.
  109. If the PortName does not exist, or the client process does not have
  110. sufficient access rights then the returned status will indicate that
  111. the port was not found.
  112. Arguments:
  113. PortHandle - A pointer to a variable that will receive the client
  114. communication port object handle value.
  115. PortName - A pointer to a port name string. The form of the name
  116. is [\name...\name]\port_name.
  117. SecurityQos - A pointer to security quality of service information
  118. to be applied to the server on the client's behalf.
  119. ClientView - An optional pointer to a structure that specifies the
  120. section that all client threads will use to send messages to the
  121. server.
  122. ClientView Structure
  123. ULONG Length - Specifies the size of this data structure in
  124. bytes.
  125. HANDLE SectionHandle - Specifies an open handle to a section
  126. object.
  127. ULONG SectionOffset - Specifies a field that will receive the
  128. actual offset, in bytes, from the start of the section. The
  129. initial value of this parameter specifies the byte offset
  130. within the section that the client's view is based. The
  131. value is rounded down to the next host page size boundary.
  132. ULONG ViewSize - Specifies a field that will receive the
  133. actual size, in bytes, of the view. If the value of this
  134. parameter is zero, then the client's view of the section
  135. will be mapped starting at the specified section offset and
  136. continuing to the end of the section. Otherwise, the
  137. initial value of this parameter specifies the size, in
  138. bytes, of the client's view and is rounded up to the next
  139. host page size boundary.
  140. PVOID ViewBase - Specifies a field that will receive the base
  141. address of the section in the client's address space.
  142. PVOID ViewRemoteBase - Specifies a field that will receive
  143. the base address of the client's section in the server's
  144. address space. Used to generate pointers that are
  145. meaningful to the server.
  146. RequiredServerSid - Optionally specifies the SID that we expect the
  147. server side of the port to possess. If not specified then we'll
  148. connect to any server SID.
  149. ServerView - An optional pointer to a structure that will receive
  150. information about the server process' view in the client's
  151. address space. The client process can use this information
  152. to validate pointers it receives from the server process.
  153. ServerView Structure
  154. ULONG Length - Specifies the size of this data structure in
  155. bytes.
  156. PVOID ViewBase - Specifies a field that will receive the base
  157. address of the server's section in the client's address
  158. space.
  159. ULONG ViewSize - Specifies a field that will receive the
  160. size, in bytes, of the server's view in the client's address
  161. space. If this field is zero, then server has no view in
  162. the client's address space.
  163. MaxMessageLength - An optional pointer to a variable that will
  164. receive maximum length of messages that can be sent to the
  165. server. The value of this parameter will not exceed
  166. MAX_PORTMSG_LENGTH bytes.
  167. ConnectionInformation - An optional pointer to uninterpreted data.
  168. This data is intended for clients to pass package, version and
  169. protocol identification information to the server to allow the
  170. server to determine if it can satisify the client before
  171. accepting the connection. Upon return to the client, the
  172. ConnectionInformation data block contains any information passed
  173. back from the server by its call to the NtCompleteConnectPort
  174. service. The output data overwrites the input data.
  175. ConnectionInformationLength - Pointer to the length of the
  176. ConnectionInformation data block. The output value is the
  177. length of the data stored in the ConnectionInformation data
  178. block by the server's call to the NtCompleteConnectPort
  179. service. This parameter is OPTIONAL only if the
  180. ConnectionInformation parameter is null, otherwise it is
  181. required.
  182. Return Value:
  183. NTSTATUS - An appropriate status value.
  184. --*/
  185. {
  186. PLPCP_PORT_OBJECT ConnectionPort;
  187. PLPCP_PORT_OBJECT ClientPort;
  188. HANDLE Handle;
  189. KPROCESSOR_MODE PreviousMode;
  190. NTSTATUS Status;
  191. ULONG ConnectionInfoLength;
  192. PVOID SectionToMap;
  193. PLPCP_MESSAGE Msg;
  194. PLPCP_CONNECTION_MESSAGE ConnectMsg;
  195. PEPROCESS CurrentProcess;
  196. PETHREAD CurrentThread = PsGetCurrentThread();
  197. LARGE_INTEGER SectionOffset;
  198. PORT_VIEW CapturedClientView;
  199. SECURITY_QUALITY_OF_SERVICE CapturedQos;
  200. PSID CapturedRequiredServerSid;
  201. PAGED_CODE();
  202. //
  203. // Get previous processor mode and probe input and output arguments if
  204. // necessary.
  205. //
  206. PreviousMode = KeGetPreviousMode();
  207. ConnectionInfoLength = 0;
  208. if (PreviousMode != KernelMode) {
  209. try {
  210. ProbeForWriteHandle( PortHandle );
  211. if (ARGUMENT_PRESENT( ClientView )) {
  212. CapturedClientView = ProbeAndReadStructure( ClientView, PORT_VIEW );
  213. if (CapturedClientView.Length != sizeof( *ClientView )) {
  214. return( STATUS_INVALID_PARAMETER );
  215. }
  216. ProbeForWriteSmallStructure( ClientView,
  217. sizeof( *ClientView ),
  218. sizeof( ULONG ));
  219. }
  220. if (ARGUMENT_PRESENT( ServerView )) {
  221. if (ProbeAndReadUlong( &ServerView->Length ) != sizeof( *ServerView )) {
  222. return( STATUS_INVALID_PARAMETER );
  223. }
  224. ProbeForWriteSmallStructure( ServerView,
  225. sizeof( *ServerView ),
  226. sizeof( ULONG ));
  227. }
  228. if (ARGUMENT_PRESENT( MaxMessageLength )) {
  229. ProbeForWriteUlong( MaxMessageLength );
  230. }
  231. if (ARGUMENT_PRESENT( ConnectionInformationLength )) {
  232. ConnectionInfoLength = ProbeAndReadUlong( ConnectionInformationLength );
  233. ProbeForWriteUlong( ConnectionInformationLength );
  234. }
  235. if (ARGUMENT_PRESENT( ConnectionInformation )) {
  236. ProbeForWrite( ConnectionInformation,
  237. ConnectionInfoLength,
  238. sizeof( UCHAR ));
  239. }
  240. CapturedQos = ProbeAndReadStructure( SecurityQos, SECURITY_QUALITY_OF_SERVICE );
  241. CapturedRequiredServerSid = RequiredServerSid;
  242. if (ARGUMENT_PRESENT( RequiredServerSid )) {
  243. Status = SeCaptureSid( RequiredServerSid,
  244. PreviousMode,
  245. NULL,
  246. 0,
  247. PagedPool,
  248. TRUE,
  249. &CapturedRequiredServerSid );
  250. if (!NT_SUCCESS(Status)) {
  251. return Status;
  252. }
  253. }
  254. } except( EXCEPTION_EXECUTE_HANDLER ) {
  255. return( GetExceptionCode() );
  256. }
  257. //
  258. // Otherwise this is a kernel mode operation
  259. //
  260. } else {
  261. if (ARGUMENT_PRESENT( ClientView )) {
  262. if (ClientView->Length != sizeof( *ClientView )) {
  263. return( STATUS_INVALID_PARAMETER );
  264. }
  265. CapturedClientView = *ClientView;
  266. }
  267. if (ARGUMENT_PRESENT( ServerView )) {
  268. if (ServerView->Length != sizeof( *ServerView )) {
  269. return( STATUS_INVALID_PARAMETER );
  270. }
  271. }
  272. if (ARGUMENT_PRESENT( ConnectionInformationLength )) {
  273. ConnectionInfoLength = *ConnectionInformationLength;
  274. }
  275. CapturedQos = *SecurityQos;
  276. CapturedRequiredServerSid = RequiredServerSid;
  277. }
  278. //
  279. // Reference the connection port object by name. Return status if
  280. // unsuccessful.
  281. //
  282. Status = ObReferenceObjectByName( PortName,
  283. 0,
  284. NULL,
  285. PORT_CONNECT,
  286. LpcPortObjectType,
  287. PreviousMode,
  288. NULL,
  289. (PVOID *)&ConnectionPort );
  290. //
  291. // If the port type object didn't work then try for a waitable port type
  292. // object
  293. //
  294. if ( Status == STATUS_OBJECT_TYPE_MISMATCH ) {
  295. Status = ObReferenceObjectByName( PortName,
  296. 0,
  297. NULL,
  298. PORT_CONNECT,
  299. LpcWaitablePortObjectType,
  300. PreviousMode,
  301. NULL,
  302. (PVOID *)&ConnectionPort );
  303. }
  304. //
  305. // We can't locate the name so release the sid if we captured one and
  306. // return error status back to our caller
  307. //
  308. if (!NT_SUCCESS( Status )) {
  309. if (CapturedRequiredServerSid != RequiredServerSid) {
  310. SeReleaseSid( CapturedRequiredServerSid, PreviousMode, TRUE);
  311. }
  312. return Status;
  313. }
  314. LpcpTrace(("Connecting to port %wZ\n", PortName ));
  315. //
  316. // Error if user didn't give us a server communication port
  317. //
  318. if ((ConnectionPort->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
  319. ObDereferenceObject( ConnectionPort );
  320. if (CapturedRequiredServerSid != RequiredServerSid) {
  321. SeReleaseSid( CapturedRequiredServerSid, PreviousMode, TRUE);
  322. }
  323. return STATUS_INVALID_PORT_HANDLE;
  324. }
  325. //
  326. // If this is NtSecureConnectPort, validated the required SID against
  327. // the SID of the server process. Fail if not equal.
  328. //
  329. if (ARGUMENT_PRESENT( RequiredServerSid )) {
  330. PTOKEN_USER TokenInfo;
  331. if (ConnectionPort->ServerProcess != NULL) {
  332. PACCESS_TOKEN Token ;
  333. Token = PsReferencePrimaryToken( ConnectionPort->ServerProcess );
  334. Status = SeQueryInformationToken( Token,
  335. TokenUser,
  336. &TokenInfo );
  337. PsDereferencePrimaryTokenEx( ConnectionPort->ServerProcess, Token );
  338. if (NT_SUCCESS( Status )) {
  339. if (!RtlEqualSid( CapturedRequiredServerSid, TokenInfo->User.Sid )) {
  340. Status = STATUS_SERVER_SID_MISMATCH;
  341. }
  342. ExFreePool( TokenInfo );
  343. }
  344. } else {
  345. Status = STATUS_SERVER_SID_MISMATCH;
  346. }
  347. //
  348. // We are all done with the required server sid if specified so
  349. // now release one if we had to capture it
  350. //
  351. if (CapturedRequiredServerSid != RequiredServerSid) {
  352. SeReleaseSid( CapturedRequiredServerSid, PreviousMode, TRUE);
  353. }
  354. //
  355. // If the se information token query didn't work then return the
  356. // error to our caller
  357. //
  358. if (!NT_SUCCESS( Status )) {
  359. ObDereferenceObject( ConnectionPort );
  360. return Status;
  361. }
  362. }
  363. //
  364. // Allocate and initialize a client communication port object. Give
  365. // the port a request message queue for lost reply datagrams. If
  366. // unable to initialize the port, then deference the port object which
  367. // will cause it to be deleted and return the system service status.
  368. //
  369. Status = ObCreateObject( PreviousMode,
  370. LpcPortObjectType,
  371. NULL,
  372. PreviousMode,
  373. NULL,
  374. FIELD_OFFSET( LPCP_PORT_OBJECT, WaitEvent ),
  375. 0,
  376. 0,
  377. (PVOID *)&ClientPort );
  378. if (!NT_SUCCESS( Status )) {
  379. ObDereferenceObject( ConnectionPort );
  380. return Status;
  381. }
  382. //
  383. // Note, that from here on, none of the error paths dereference the
  384. // connection port pointer, just the newly created client port pointer.
  385. // The port delete routine will get called when the client port is
  386. // deleted and it will dereference the connection port pointer stored
  387. // in the client port object.
  388. //
  389. //
  390. // Initialize the client port object to zeros and then fill in the
  391. // fields.
  392. //
  393. RtlZeroMemory( ClientPort, FIELD_OFFSET( LPCP_PORT_OBJECT, WaitEvent ));
  394. ClientPort->Flags = CLIENT_COMMUNICATION_PORT;
  395. ClientPort->ConnectionPort = ConnectionPort;
  396. ClientPort->MaxMessageLength = ConnectionPort->MaxMessageLength;
  397. ClientPort->SecurityQos = CapturedQos;
  398. InitializeListHead( &ClientPort->LpcReplyChainHead );
  399. InitializeListHead( &ClientPort->LpcDataInfoChainHead );
  400. //
  401. // Set the security tracking mode, and initialize the client security
  402. // context if it is static tracking.
  403. //
  404. if (CapturedQos.ContextTrackingMode == SECURITY_DYNAMIC_TRACKING) {
  405. ClientPort->Flags |= PORT_DYNAMIC_SECURITY;
  406. } else {
  407. Status = SeCreateClientSecurity( CurrentThread,
  408. &CapturedQos,
  409. FALSE,
  410. &ClientPort->StaticSecurity );
  411. if (!NT_SUCCESS( Status )) {
  412. ObDereferenceObject( ClientPort );
  413. return Status;
  414. }
  415. }
  416. //
  417. // Client communication ports get a request message queue for lost
  418. // replies.
  419. //
  420. Status = LpcpInitializePortQueue( ClientPort );
  421. if (!NT_SUCCESS( Status )) {
  422. ObDereferenceObject( ClientPort );
  423. return Status;
  424. }
  425. //
  426. // If client has allocated a port memory section, then map a view of
  427. // that section into the client's address space. Also reference the
  428. // section object so we can pass a pointer to the section object in
  429. // connection request message. If the server accepts the connection,
  430. // then it will map a corresponding view of the section in the server's
  431. // address space, using the referenced pointer passed in the connection
  432. // request message.
  433. //
  434. if (ARGUMENT_PRESENT( ClientView )) {
  435. Status = ObReferenceObjectByHandle( CapturedClientView.SectionHandle,
  436. SECTION_MAP_READ |
  437. SECTION_MAP_WRITE,
  438. MmSectionObjectType,
  439. PreviousMode,
  440. (PVOID *)&SectionToMap,
  441. NULL );
  442. if (!NT_SUCCESS( Status )) {
  443. ObDereferenceObject( ClientPort );
  444. return Status;
  445. }
  446. SectionOffset.LowPart = CapturedClientView.SectionOffset,
  447. SectionOffset.HighPart = 0;
  448. CurrentProcess = PsGetCurrentProcess();
  449. //
  450. // Now map a view of the section using the reference we just captured
  451. // and not the section handle itself, because the handle may have changed
  452. //
  453. Status = MmMapViewOfSection( SectionToMap,
  454. CurrentProcess,
  455. &ClientPort->ClientSectionBase,
  456. 0,
  457. 0,
  458. &SectionOffset,
  459. &CapturedClientView.ViewSize,
  460. ViewUnmap,
  461. 0,
  462. PAGE_READWRITE );
  463. CapturedClientView.SectionOffset = SectionOffset.LowPart;
  464. if (!NT_SUCCESS( Status )) {
  465. ObDereferenceObject( SectionToMap );
  466. ObDereferenceObject( ClientPort );
  467. return Status;
  468. }
  469. CapturedClientView.ViewBase = ClientPort->ClientSectionBase;
  470. //
  471. // We'll add an extra-reference to the current process, when we have
  472. // a section mapped in that process.
  473. //
  474. ClientPort->MappingProcess = CurrentProcess;
  475. ObReferenceObject( ClientPort->MappingProcess );
  476. } else {
  477. SectionToMap = NULL;
  478. }
  479. //
  480. // Adjust the size of the connection info length that the client supplied
  481. // to be the no longer than one the connection port will accept
  482. //
  483. if (ConnectionInfoLength > ConnectionPort->MaxConnectionInfoLength) {
  484. ConnectionInfoLength = ConnectionPort->MaxConnectionInfoLength;
  485. }
  486. //
  487. // At this point the client port is all setup and now we have to
  488. // allocate a request connection message for the server and send it off
  489. //
  490. // Allocate a connection request message. It holds the LPCP message,
  491. // the LPCP connection message, and the user supplied connection
  492. // information
  493. //
  494. Msg = LpcpAllocateFromPortZone( sizeof( *Msg ) +
  495. sizeof( *ConnectMsg ) +
  496. ConnectionInfoLength );
  497. //
  498. // If we didn't get memory for the message then tell our caller we failed
  499. //
  500. if (Msg == NULL) {
  501. if (SectionToMap != NULL) {
  502. ObDereferenceObject( SectionToMap );
  503. }
  504. ObDereferenceObject( ClientPort );
  505. return STATUS_NO_MEMORY;
  506. }
  507. //
  508. // Msg points to the LPCP message, followed by ConnectMsg which points to
  509. // the LPCP connection message, followed by client specified information.
  510. // We'll now fill it all in.
  511. //
  512. ConnectMsg = (PLPCP_CONNECTION_MESSAGE)(Msg + 1);
  513. //
  514. // This thread originated the message
  515. //
  516. Msg->Request.ClientId = CurrentThread->Cid;
  517. //
  518. // If we have a client view then copy over the client view information
  519. // otherwise we'll zero out all of the view information
  520. //
  521. if (ARGUMENT_PRESENT( ClientView )) {
  522. Msg->Request.ClientViewSize = CapturedClientView.ViewSize;
  523. RtlCopyMemory( &ConnectMsg->ClientView,
  524. &CapturedClientView,
  525. sizeof( CapturedClientView ));
  526. RtlZeroMemory( &ConnectMsg->ServerView, sizeof( ConnectMsg->ServerView ));
  527. } else {
  528. Msg->Request.ClientViewSize = 0;
  529. RtlZeroMemory( ConnectMsg, sizeof( *ConnectMsg ));
  530. }
  531. ConnectMsg->ClientPort = NULL; // Set below
  532. ConnectMsg->SectionToMap = SectionToMap;
  533. //
  534. // The data length is everything after the port message within the lpcp
  535. // message. In other words the connection message and the user supplied
  536. // information
  537. //
  538. Msg->Request.u1.s1.DataLength = (CSHORT)(sizeof( *ConnectMsg ) +
  539. ConnectionInfoLength);
  540. //
  541. // The total length add on the LPCP message
  542. //
  543. Msg->Request.u1.s1.TotalLength = (CSHORT)(sizeof( *Msg ) +
  544. Msg->Request.u1.s1.DataLength);
  545. //
  546. // This will be a connection request message
  547. //
  548. Msg->Request.u2.s2.Type = LPC_CONNECTION_REQUEST;
  549. //
  550. // If the caller supplied some connection information then copy
  551. // that into place right now
  552. //
  553. if (ARGUMENT_PRESENT( ConnectionInformation )) {
  554. try {
  555. RtlCopyMemory( ConnectMsg + 1,
  556. ConnectionInformation,
  557. ConnectionInfoLength );
  558. } except( EXCEPTION_EXECUTE_HANDLER ) {
  559. //
  560. // If we fail then cleanup after ourselves and return the
  561. // error to our caller
  562. //
  563. LpcpFreeToPortZone( Msg, 0 );
  564. if (SectionToMap != NULL) {
  565. ObDereferenceObject( SectionToMap );
  566. }
  567. ObDereferenceObject( ClientPort );
  568. return GetExceptionCode();
  569. }
  570. }
  571. //
  572. // The message is mostly ready to go now put it on the servers queue.
  573. //
  574. // Acquire the mutex that guards the LpcReplyMessage field of the
  575. // thread. Also acquire the semaphore that guards the connection
  576. // request message queue. Stamp the connection request message with
  577. // a serial number, insert the message at the tail of the connection
  578. // request message queue and remember the address of the message in
  579. // the LpcReplyMessage field for the current thread.
  580. //
  581. Status = STATUS_SUCCESS;
  582. LpcpAcquireLpcpLockByThread(CurrentThread);
  583. //
  584. // See if the port name has been deleted from under us. If so, then
  585. // don't queue the message and don't wait for a reply
  586. //
  587. if (ConnectionPort->Flags & PORT_NAME_DELETED) {
  588. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  589. } else {
  590. LpcpTrace(( "Send Connect Msg %lx to Port %wZ (%lx)\n", Msg, PortName, ConnectionPort ));
  591. //
  592. // Stamp the request message with a serial number, insert the message
  593. // at the tail of the request message queue
  594. //
  595. Msg->RepliedToThread = NULL;
  596. Msg->Request.MessageId = LpcpGenerateMessageId();
  597. CurrentThread->LpcReplyMessageId = Msg->Request.MessageId;
  598. InsertTailList( &ConnectionPort->MsgQueue.ReceiveHead, &Msg->Entry );
  599. InsertTailList( &ConnectionPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
  600. CurrentThread->LpcReplyMessage = Msg;
  601. //
  602. // Reference the port we are passing in the connect msg so if we die
  603. // it will still be valid for the server in NtAcceptConnectPort. The
  604. // reference will be released when the message is freed.
  605. //
  606. ObReferenceObject( ClientPort );
  607. ConnectMsg->ClientPort = ClientPort;
  608. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  609. }
  610. LpcpReleaseLpcpLock();
  611. //
  612. // At this point the client's communication port is all set up and the
  613. // connection request message is in the server's queue. So now we have
  614. // to single the server and wait for a reply
  615. //
  616. if (NT_SUCCESS( Status )) {
  617. //
  618. // If this is a waitable port then set the event that they might be
  619. // waiting on
  620. //
  621. if ( ConnectionPort->Flags & PORT_WAITABLE ) {
  622. KeSetEvent( &ConnectionPort->WaitEvent, 1, FALSE );
  623. }
  624. //
  625. // Increment the connection request message queue semaphore by one for
  626. // the newly inserted connection request message. Release the spin
  627. // locks, while remaining at the dispatcher IRQL. Then wait for the
  628. // reply to this connection request by waiting on the LpcReplySemaphore
  629. // for the current thread.
  630. //
  631. KeReleaseSemaphore( ConnectionPort->MsgQueue.Semaphore,
  632. 1,
  633. 1,
  634. FALSE );
  635. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  636. Status = KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
  637. Executive,
  638. PreviousMode,
  639. FALSE,
  640. NULL );
  641. }
  642. if (Status == STATUS_USER_APC) {
  643. //
  644. // if the semaphore is signaled, then clear it
  645. //
  646. if (KeReadStateSemaphore( &CurrentThread->LpcReplySemaphore )) {
  647. KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
  648. WrExecutive,
  649. KernelMode,
  650. FALSE,
  651. NULL );
  652. Status = STATUS_SUCCESS;
  653. }
  654. }
  655. //
  656. // A connection request is accepted if the ConnectedPort of the client's
  657. // communication port has been filled in.
  658. //
  659. if (Status == STATUS_SUCCESS) {
  660. SectionToMap = LpcpFreeConMsg( &Msg, &ConnectMsg, CurrentThread );
  661. //
  662. // Check that we got a reply message
  663. //
  664. if (Msg != NULL) {
  665. //
  666. // Copy any connection information back to the caller, but first
  667. // calculate the new connection data length for the reply and
  668. // don't let it grow beyond what we probed originally
  669. //
  670. if ((Msg->Request.u1.s1.DataLength - sizeof( *ConnectMsg )) < ConnectionInfoLength) {
  671. ConnectionInfoLength = Msg->Request.u1.s1.DataLength - sizeof( *ConnectMsg );
  672. }
  673. if (ARGUMENT_PRESENT( ConnectionInformation )) {
  674. try {
  675. if (ARGUMENT_PRESENT( ConnectionInformationLength )) {
  676. *ConnectionInformationLength = ConnectionInfoLength;
  677. }
  678. RtlCopyMemory( ConnectionInformation,
  679. ConnectMsg + 1,
  680. ConnectionInfoLength );
  681. } except( EXCEPTION_EXECUTE_HANDLER ) {
  682. Status = GetExceptionCode();
  683. }
  684. }
  685. //
  686. // Insert client communication port object in specified object
  687. // table. Set port handle value if successful. If not
  688. // successful, then the port will have been dereferenced, which
  689. // will cause it to be freed, after our delete procedure is
  690. // called. The delete procedure will undo the work done to
  691. // initialize the port.
  692. //
  693. if (ClientPort->ConnectedPort != NULL) {
  694. ULONG CapturedMaxMessageLength;
  695. //
  696. // Before we do the object insert we need to get the max
  697. // message length because right after the call the object
  698. // could be dereferenced and gone away
  699. //
  700. CapturedMaxMessageLength = ConnectionPort->MaxMessageLength;
  701. //
  702. // Now create a handle for the new client port object.
  703. //
  704. Status = ObInsertObject( ClientPort,
  705. NULL,
  706. PORT_ALL_ACCESS,
  707. 0,
  708. (PVOID *)NULL,
  709. &Handle );
  710. if (NT_SUCCESS( Status )) {
  711. //
  712. // This is the only successful path through this routine.
  713. // Set the output variables, later we'll free the msg
  714. // back to the port zone and return to our caller
  715. //
  716. try {
  717. *PortHandle = Handle;
  718. if (ARGUMENT_PRESENT( MaxMessageLength )) {
  719. *MaxMessageLength = CapturedMaxMessageLength;
  720. }
  721. if (ARGUMENT_PRESENT( ClientView )) {
  722. RtlCopyMemory( ClientView,
  723. &ConnectMsg->ClientView,
  724. sizeof( *ClientView ));
  725. }
  726. if (ARGUMENT_PRESENT( ServerView )) {
  727. RtlCopyMemory( ServerView,
  728. &ConnectMsg->ServerView,
  729. sizeof( *ServerView ));
  730. }
  731. } except( EXCEPTION_EXECUTE_HANDLER ) {
  732. Status = GetExceptionCode();
  733. NtClose( Handle );
  734. }
  735. }
  736. } else {
  737. //
  738. // Otherwise we did not get a connect port from the server so
  739. // the connection was refused
  740. //
  741. LpcpTrace(( "Connection request refused.\n" ));
  742. if ( SectionToMap != NULL ) {
  743. ObDereferenceObject( SectionToMap );
  744. }
  745. //
  746. // Synchronize with the deletion path for the port object
  747. // If the server accepted the connection and immediately
  748. // closed the server handle, the ConnectionPort field will be NULL.
  749. // If the server closed the connection port as well, the captured
  750. // value for the connection port will be invalid.
  751. //
  752. LpcpAcquireLpcpLockByThread(CurrentThread);
  753. if ((ClientPort->ConnectionPort == NULL)
  754. ||
  755. (ConnectionPort->Flags & PORT_NAME_DELETED)) {
  756. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  757. } else {
  758. Status = STATUS_PORT_CONNECTION_REFUSED;
  759. }
  760. LpcpReleaseLpcpLock();
  761. ObDereferenceObject( ClientPort );
  762. }
  763. //
  764. // Free the reply message back to the port zone
  765. //
  766. LpcpFreeToPortZone( Msg, 0 );
  767. } else {
  768. //
  769. // We did not get a reply message so the connection must have
  770. // been refused
  771. //
  772. if (SectionToMap != NULL) {
  773. ObDereferenceObject( SectionToMap );
  774. }
  775. ObDereferenceObject( ClientPort );
  776. Status = STATUS_PORT_CONNECTION_REFUSED;
  777. }
  778. } else {
  779. //
  780. // Our wait was not successful
  781. //
  782. //
  783. // Remove the connection request message from the received
  784. // queue and free the message back to the connection
  785. // port's zone.
  786. //
  787. SectionToMap = LpcpFreeConMsg( &Msg, &ConnectMsg, CurrentThread );
  788. //
  789. // The wait was not successful, but in the meantime the server could
  790. // replied, so it signaled the lpc semaphore. We have to clear the
  791. // semaphore state right now.
  792. //
  793. if (KeReadStateSemaphore( &CurrentThread->LpcReplySemaphore )) {
  794. KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
  795. WrExecutive,
  796. KernelMode,
  797. FALSE,
  798. NULL );
  799. }
  800. if (Msg != NULL) {
  801. LpcpFreeToPortZone( Msg, 0 );
  802. }
  803. //
  804. // If a client section was specified, then dereference the section
  805. // object.
  806. //
  807. if ( SectionToMap != NULL ) {
  808. ObDereferenceObject( SectionToMap );
  809. }
  810. //
  811. // If the connection was rejected or the wait failed, then
  812. // dereference the client port object, which will cause it to
  813. // be deleted.
  814. //
  815. ObDereferenceObject( ClientPort );
  816. }
  817. //
  818. // And return to our caller
  819. //
  820. return Status;
  821. }
  822. //
  823. // Local support routine
  824. //
  825. PVOID
  826. LpcpFreeConMsg (
  827. IN PLPCP_MESSAGE *Msg,
  828. PLPCP_CONNECTION_MESSAGE *ConnectMsg,
  829. IN PETHREAD CurrentThread
  830. )
  831. /*++
  832. Routine Description:
  833. This routine returns a connection reply message for the specified thread
  834. Arguments:
  835. Msg - Receives a pointer to the LPCP message if there is a reply
  836. ConnectMsg - Receives a pointer to the LPCP connection message if there
  837. is a reply
  838. CurrentThread - Specifies the thread we're to be examining
  839. Return Value:
  840. PVOID - Returns a pointer to the section to map in the connection message
  841. --*/
  842. {
  843. PVOID SectionToMap;
  844. PLPCP_MESSAGE LpcMessage;
  845. //
  846. // Acquire the LPC mutex, remove the connection request message
  847. // from the received queue and free the message back to the connection
  848. // port's zone.
  849. //
  850. LpcpAcquireLpcpLock();
  851. //
  852. // Remove the thread from the reply rundown list in case we did not wakeup due to
  853. // a reply
  854. //
  855. if (!IsListEmpty( &CurrentThread->LpcReplyChain )) {
  856. RemoveEntryList( &CurrentThread->LpcReplyChain );
  857. InitializeListHead( &CurrentThread->LpcReplyChain );
  858. }
  859. //
  860. // Check if the thread has an LPC reply message waiting to be handled
  861. //
  862. LpcMessage = LpcpGetThreadMessage(CurrentThread);
  863. if (LpcMessage != NULL) {
  864. //
  865. // Take the message off the threads list
  866. //
  867. *Msg = LpcMessage;
  868. if (!IsListEmpty( &LpcMessage->Entry )) {
  869. RemoveEntryList( &LpcMessage->Entry );
  870. InitializeListHead( &LpcMessage->Entry );
  871. }
  872. CurrentThread->LpcReplyMessage = NULL;
  873. CurrentThread->LpcReplyMessageId = 0;
  874. //
  875. // Set the connection message pointer, and copy over the section
  876. // to map location before zeroing it out
  877. //
  878. *ConnectMsg = (PLPCP_CONNECTION_MESSAGE)(LpcMessage + 1);
  879. SectionToMap = (*ConnectMsg)->SectionToMap;
  880. (*ConnectMsg)->SectionToMap = NULL;
  881. } else {
  882. //
  883. // Otherwise there is no LPC message to be handle so we'll return
  884. // null's to our caller
  885. //
  886. *Msg = NULL;
  887. SectionToMap = NULL;
  888. }
  889. //
  890. // Release the global lock and return to our caller
  891. //
  892. LpcpReleaseLpcpLock();
  893. return SectionToMap;
  894. }