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.

793 lines
21 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. smloop.c
  5. Abstract:
  6. Session Manager Listen and API loops
  7. Author:
  8. Mark Lucovsky (markl) 04-Oct-1989
  9. Revision History:
  10. --*/
  11. #include "smsrvp.h"
  12. #include <ntosp.h> // Only for the interlocked functions.
  13. #define SM_WORKER_THREADS_LIMIT 4
  14. ULONG_PTR SmUniqueProcessId;
  15. LONG SmpCurrentThreadsLimit = SM_WORKER_THREADS_LIMIT;
  16. LONG SmWorkerThreadsAvailable = 0;
  17. LONG SmTotalApiThreads = 0;
  18. PSMP_CLIENT_CONTEXT SmpDeferredFreeList = NULL;
  19. NTSTATUS
  20. SmpHandleConnectionRequest(
  21. IN HANDLE ConnectionPort,
  22. IN PSBAPIMSG Message
  23. );
  24. PSMAPI SmpApiDispatch[SmMaxApiNumber] = {
  25. SmpCreateForeignSession,
  26. SmpSessionComplete,
  27. SmpTerminateForeignSession,
  28. SmpExecPgm,
  29. SmpLoadDeferedSubsystem,
  30. SmpStartCsr,
  31. SmpStopCsr
  32. };
  33. #if DBG
  34. PSZ SmpApiName[ SmMaxApiNumber+1 ] = {
  35. "SmCreateForeignSession",
  36. "SmSessionComplete",
  37. "SmTerminateForeignSession",
  38. "SmExecPgm",
  39. "SmLoadDeferedSubsystem",
  40. "SmStartCsr",
  41. "SmStopCsr",
  42. "Unknown Sm Api Number"
  43. };
  44. #endif // DBG
  45. EXCEPTION_DISPOSITION
  46. DbgpUnhandledExceptionFilter(
  47. struct _EXCEPTION_POINTERS *ExceptionInfo
  48. );
  49. VOID
  50. SmpFlushDeferredList()
  51. {
  52. PSMP_CLIENT_CONTEXT Head = SmpDeferredFreeList;
  53. SmpDeferredFreeList = NULL;
  54. while (Head != NULL) {
  55. PSMP_CLIENT_CONTEXT ClientContext = Head;
  56. NTSTATUS Status;
  57. Head = Head->Link;
  58. if (ClientContext->ClientProcessHandle) {
  59. Status = NtClose( ClientContext->ClientProcessHandle );
  60. ASSERT(NT_SUCCESS(Status));
  61. }
  62. Status = NtClose( ClientContext->ServerPortHandle );
  63. ASSERT(NT_SUCCESS(Status));
  64. RtlFreeHeap( SmpHeap, 0, ClientContext );
  65. }
  66. SmpCurrentThreadsLimit = SM_WORKER_THREADS_LIMIT;
  67. }
  68. VOID
  69. SmpPushDeferredClientContext(
  70. PSMP_CLIENT_CONTEXT ClientContext
  71. )
  72. {
  73. PSMP_CLIENT_CONTEXT CapturedHead;
  74. do {
  75. CapturedHead = SmpDeferredFreeList;
  76. ClientContext->Link = CapturedHead;
  77. } while ( InterlockedCompareExchangePointer(&SmpDeferredFreeList, ClientContext, CapturedHead) != CapturedHead );
  78. SmpCurrentThreadsLimit = 1;
  79. }
  80. NTSTATUS
  81. SmpApiLoop (
  82. IN PVOID ThreadParameter
  83. )
  84. /*++
  85. Routine Description:
  86. This is the main Session Manager API Loop. It
  87. services session manager API requests.
  88. Arguments:
  89. ThreadParameter - Supplies a handle to the API port used
  90. to receive session manager API requests.
  91. Return Value:
  92. None.
  93. --*/
  94. {
  95. PSMAPIMSG SmApiReplyMsg;
  96. SMMESSAGE_SIZE MsgBuf;
  97. PSMAPIMSG SmApiMsg;
  98. NTSTATUS Status;
  99. HANDLE ConnectionPort;
  100. PSMP_CLIENT_CONTEXT ClientContext;
  101. PSMPKNOWNSUBSYS KnownSubSys;
  102. PROCESS_BASIC_INFORMATION ProcessInfo;
  103. InterlockedIncrement(&SmTotalApiThreads);
  104. RtlSetThreadIsCritical(TRUE, NULL, TRUE);
  105. NtQueryInformationProcess( NtCurrentProcess(),
  106. ProcessBasicInformation,
  107. &ProcessInfo,
  108. sizeof(ProcessInfo),
  109. NULL );
  110. SmUniqueProcessId = ProcessInfo.UniqueProcessId;
  111. ConnectionPort = (HANDLE) ThreadParameter;
  112. SmApiMsg = (PSMAPIMSG)&MsgBuf;
  113. SmApiReplyMsg = NULL;
  114. try {
  115. for(;;) {
  116. {
  117. LONG CapturedThreads;
  118. do {
  119. CapturedThreads = SmWorkerThreadsAvailable;
  120. if (CapturedThreads >= SmpCurrentThreadsLimit) {
  121. if (SmApiReplyMsg) {
  122. Status = NtReplyPort(
  123. ConnectionPort,
  124. (PPORT_MESSAGE) SmApiReplyMsg
  125. );
  126. }
  127. InterlockedDecrement(&SmTotalApiThreads);
  128. RtlSetThreadIsCritical(FALSE, NULL, TRUE);
  129. RtlExitUserThread(STATUS_SUCCESS);
  130. //
  131. // RtlExitUserThread never returns
  132. //
  133. }
  134. } while ( InterlockedCompareExchange(&SmWorkerThreadsAvailable, CapturedThreads + 1, CapturedThreads) != CapturedThreads);
  135. }
  136. if (SmTotalApiThreads == 1) {
  137. SmpFlushDeferredList();
  138. }
  139. Status = NtReplyWaitReceivePort(
  140. ConnectionPort,
  141. (PVOID *) &ClientContext,
  142. (PPORT_MESSAGE) SmApiReplyMsg,
  143. (PPORT_MESSAGE) SmApiMsg
  144. );
  145. //
  146. // Launching at the same time a several subsystems can deadlock smss
  147. // if it has only two worker threads.
  148. // We create more threads if there is no server thread available
  149. //
  150. if (InterlockedDecrement(&SmWorkerThreadsAvailable) == 0) {
  151. NTSTATUS st = RtlCreateUserThread(
  152. NtCurrentProcess(),
  153. NULL,
  154. FALSE,
  155. 0L,
  156. 0L,
  157. 0L,
  158. SmpApiLoop,
  159. (PVOID) ThreadParameter,
  160. NULL,
  161. NULL
  162. );
  163. }
  164. if ( !NT_SUCCESS(Status) ) {
  165. SmApiReplyMsg = NULL;
  166. continue;
  167. } else if ( SmApiMsg->h.u2.s2.Type == LPC_CONNECTION_REQUEST ) {
  168. SmpHandleConnectionRequest( ConnectionPort,
  169. (PSBAPIMSG) SmApiMsg
  170. );
  171. SmApiReplyMsg = NULL;
  172. } else if ( SmApiMsg->h.u2.s2.Type == LPC_PORT_CLOSED ) {
  173. if (ClientContext) {
  174. SmpPushDeferredClientContext(ClientContext);
  175. }
  176. SmApiReplyMsg = NULL;
  177. } else {
  178. if ( !ClientContext ) {
  179. SmApiReplyMsg = NULL;
  180. continue;
  181. }
  182. KnownSubSys = ClientContext->KnownSubSys;
  183. SmApiMsg->ReturnedStatus = STATUS_PENDING;
  184. if ((ULONG) SmApiMsg->ApiNumber >= (ULONG) SmMaxApiNumber ) {
  185. Status = STATUS_NOT_IMPLEMENTED;
  186. } else {
  187. switch (SmApiMsg->ApiNumber) {
  188. case SmExecPgmApi :
  189. Status = (SmpApiDispatch[SmApiMsg->ApiNumber])(
  190. SmApiMsg,
  191. ClientContext,
  192. ConnectionPort);
  193. break;
  194. case SmLoadDeferedSubsystemApi :
  195. Status = (SmpApiDispatch[SmApiMsg->ApiNumber])(
  196. SmApiMsg,
  197. ClientContext,
  198. ConnectionPort);
  199. break;
  200. case SmStopCsrApi :
  201. case SmStartCsrApi :
  202. //
  203. // These Api's can only be called from a system process
  204. //
  205. if (ClientContext->SecurityContext == UNKNOWN_CONTEXT) {
  206. //
  207. // Initialize the client security context
  208. //
  209. ClientContext->SecurityContext =
  210. SmpClientSecurityContext ((PPORT_MESSAGE)SmApiMsg,
  211. ClientContext->ServerPortHandle);
  212. }
  213. if (ClientContext->SecurityContext == SYSTEM_CONTEXT) {
  214. Status = (SmpApiDispatch[SmApiMsg->ApiNumber])(
  215. SmApiMsg,
  216. ClientContext,
  217. ConnectionPort);
  218. } else {
  219. #if DBG
  220. KdPrint(("SMSS: Calling Sm Terminal Server Api from Non-System context.Status = STATUS_ACCESS_DENIED\n"));
  221. #endif
  222. Status = STATUS_ACCESS_DENIED;
  223. }
  224. break;
  225. case SmCreateForeignSessionApi :
  226. case SmSessionCompleteApi :
  227. case SmTerminateForeignSessionApi :
  228. if (!KnownSubSys) {
  229. Status = STATUS_INVALID_PARAMETER;
  230. } else {
  231. Status =
  232. (SmpApiDispatch[SmApiMsg->ApiNumber])(
  233. SmApiMsg,
  234. ClientContext,
  235. ConnectionPort);
  236. }
  237. break;
  238. }
  239. }
  240. SmApiMsg->ReturnedStatus = Status;
  241. SmApiReplyMsg = SmApiMsg;
  242. }
  243. }
  244. } except (DbgpUnhandledExceptionFilter( GetExceptionInformation() )) {
  245. ;
  246. }
  247. //
  248. // Make the compiler happy
  249. //
  250. return STATUS_UNSUCCESSFUL;
  251. }
  252. NTSTATUS
  253. SmpHandleConnectionRequest(
  254. IN HANDLE ConnectionPort,
  255. IN PSBAPIMSG Message
  256. )
  257. /*++
  258. Routine Description:
  259. This routine handles connection requests from either known subsystems,
  260. or other clients. Other clients are admin processes.
  261. The protocol for connection from a known subsystem is:
  262. capture the name of the subsystem's Sb API port
  263. Accept the connection
  264. Connect to the subsystems Sb API port
  265. Store the communication port handle in the known subsystem database
  266. signal the event associated with the known subsystem
  267. The protocol for others is to simply validate and accept the connection
  268. request.
  269. Arguments:
  270. Return Value:
  271. None.
  272. --*/
  273. {
  274. NTSTATUS st;
  275. HANDLE CommunicationPort;
  276. REMOTE_PORT_VIEW ClientView;
  277. PSBCONNECTINFO ConnectInfo;
  278. ULONG ConnectInfoLength;
  279. PSMPKNOWNSUBSYS KnownSubSys, KnownSubSys2;
  280. BOOLEAN Accept;
  281. UNICODE_STRING SubSystemPort;
  282. SECURITY_QUALITY_OF_SERVICE DynamicQos;
  283. PSMP_CLIENT_CONTEXT ClientContext;
  284. OBJECT_ATTRIBUTES ObjA;
  285. HANDLE ClientProcessHandle=NULL;
  286. ULONG MuSessionId = 0;
  287. //
  288. // Set up the security quality of service parameters to use over the
  289. // sb API port. Use the most efficient (least overhead) - which is dynamic
  290. // rather than static tracking.
  291. //
  292. DynamicQos.ImpersonationLevel = SecurityIdentification;
  293. DynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
  294. DynamicQos.EffectiveOnly = TRUE;
  295. Accept = TRUE; // Assume we will accept
  296. //
  297. // Get MuSessionId of the client if session manager is not connecting to itself
  298. //
  299. if ( (ULONG_PTR) Message->h.ClientId.UniqueProcess == SmUniqueProcessId ) {
  300. KnownSubSys = NULL;
  301. ClientProcessHandle = NULL;
  302. } else {
  303. InitializeObjectAttributes( &ObjA, NULL, 0, NULL, NULL );
  304. st = NtOpenProcess( &ClientProcessHandle, PROCESS_QUERY_INFORMATION,
  305. &ObjA, &Message->h.ClientId );
  306. if (NT_SUCCESS (st)) {
  307. SmpGetProcessMuSessionId( ClientProcessHandle, &MuSessionId );
  308. } else {
  309. Accept = FALSE;
  310. }
  311. }
  312. ConnectInfo = &Message->ConnectionRequest;
  313. KnownSubSys = SmpLocateKnownSubSysByCid(&Message->h.ClientId);
  314. if ( (KnownSubSys) && (Accept == TRUE) ) {
  315. KnownSubSys2 = SmpLocateKnownSubSysByType(MuSessionId, ConnectInfo->SubsystemImageType);
  316. if (KnownSubSys2 == KnownSubSys ) {
  317. Accept = FALSE;
  318. KdPrint(("SMSS: Connection from SubSystem rejected\n"));
  319. KdPrint(("SMSS: Image type already being served\n"));
  320. } else {
  321. KnownSubSys->ImageType = ConnectInfo->SubsystemImageType;
  322. }
  323. if (KnownSubSys2) {
  324. RtlEnterCriticalSection( &SmpKnownSubSysLock );
  325. SmpDeferenceKnownSubSys(KnownSubSys2);
  326. RtlLeaveCriticalSection( &SmpKnownSubSysLock );
  327. }
  328. }
  329. if (Accept) {
  330. ClientContext = RtlAllocateHeap(SmpHeap, MAKE_TAG( SM_TAG ), sizeof(SMP_CLIENT_CONTEXT));
  331. if ( ClientContext ) {
  332. ClientContext->ClientProcessHandle = ClientProcessHandle;
  333. ClientContext->KnownSubSys = KnownSubSys;
  334. //
  335. // The sm apis used by Terminal Server to start and stop CSR
  336. // do not get called from known subsystems and are restricted
  337. // to system processes only.
  338. //
  339. ClientContext->SecurityContext = UNKNOWN_CONTEXT;
  340. ClientContext->ServerPortHandle = NULL;
  341. } else {
  342. Accept = FALSE;
  343. }
  344. }
  345. ClientView.Length = sizeof(ClientView);
  346. st = NtAcceptConnectPort(
  347. &CommunicationPort,
  348. ClientContext,
  349. (PPORT_MESSAGE)Message,
  350. Accept,
  351. NULL,
  352. &ClientView
  353. );
  354. if ( Accept ) {
  355. if (NT_SUCCESS (st)) {
  356. if (ClientContext) {
  357. ClientContext->ServerPortHandle = CommunicationPort;
  358. }
  359. if ( KnownSubSys ) {
  360. KnownSubSys->SmApiCommunicationPort = CommunicationPort;
  361. }
  362. st = NtCompleteConnectPort(CommunicationPort);
  363. if (!NT_SUCCESS(st)) {
  364. if ( KnownSubSys ) {
  365. KnownSubSys->SmApiCommunicationPort = NULL;
  366. RtlEnterCriticalSection( &SmpKnownSubSysLock );
  367. SmpDeferenceKnownSubSys(KnownSubSys);
  368. RtlLeaveCriticalSection( &SmpKnownSubSysLock );
  369. }
  370. return st;
  371. }
  372. //
  373. // Connect Back to subsystem.
  374. //
  375. if ( KnownSubSys ) {
  376. ConnectInfo->EmulationSubSystemPortName[
  377. sizeof (ConnectInfo->EmulationSubSystemPortName)/sizeof (WCHAR) - 1] = '\0';
  378. RtlCreateUnicodeString( &SubSystemPort,
  379. ConnectInfo->EmulationSubSystemPortName
  380. );
  381. ConnectInfoLength = sizeof( *ConnectInfo );
  382. st = NtConnectPort(
  383. &KnownSubSys->SbApiCommunicationPort,
  384. &SubSystemPort,
  385. &DynamicQos,
  386. NULL,
  387. NULL,
  388. NULL,
  389. NULL,
  390. NULL
  391. );
  392. if ( !NT_SUCCESS(st) ) {
  393. KdPrint(("SMSS: Connect back to Sb %wZ failed %lx\n",&SubSystemPort,st));
  394. }
  395. RtlFreeUnicodeString( &SubSystemPort );
  396. NtSetEvent(KnownSubSys->Active,NULL);
  397. }
  398. } else {
  399. if (ClientProcessHandle) {
  400. NtClose( ClientProcessHandle );
  401. }
  402. RtlFreeHeap( SmpHeap, 0, ClientContext );
  403. }
  404. } else {
  405. if (ClientProcessHandle) {
  406. NtClose( ClientProcessHandle );
  407. }
  408. }
  409. if (KnownSubSys) {
  410. RtlEnterCriticalSection( &SmpKnownSubSysLock );
  411. SmpDeferenceKnownSubSys(KnownSubSys);
  412. RtlLeaveCriticalSection( &SmpKnownSubSysLock );
  413. }
  414. return st;
  415. }
  416. PSMPKNOWNSUBSYS
  417. SmpLocateKnownSubSysByCid(
  418. IN PCLIENT_ID ClientId
  419. )
  420. /*++
  421. Routine Description:
  422. This function scans the known subsystem table looking for
  423. a matching client id (just UniqueProcess portion). If found,
  424. than the connection request is from a known subsystem and
  425. accept is always granted. Otherwise, it must be an administrative
  426. process.
  427. Arguments:
  428. ClientId - Supplies the ClientId whose UniqueProcess field is to be used
  429. in the known subsystem scan.
  430. Return Value:
  431. NULL - The ClientId does not match a known subsystem.
  432. NON-NULL - Returns the address of the known subsystem.
  433. --*/
  434. {
  435. PSMPKNOWNSUBSYS KnownSubSys = NULL;
  436. PLIST_ENTRY Next;
  437. //
  438. // Acquire known subsystem lock.
  439. //
  440. RtlEnterCriticalSection(&SmpKnownSubSysLock);
  441. Next = SmpKnownSubSysHead.Flink;
  442. while ( Next != &SmpKnownSubSysHead ) {
  443. KnownSubSys = CONTAINING_RECORD(Next,SMPKNOWNSUBSYS,Links);
  444. Next = Next->Flink;
  445. if ( (KnownSubSys->InitialClientId.UniqueProcess == ClientId->UniqueProcess) &&
  446. !KnownSubSys->Deleting ) {
  447. SmpReferenceKnownSubSys(KnownSubSys);
  448. break;
  449. } else {
  450. KnownSubSys = NULL;
  451. }
  452. }
  453. //
  454. // Unlock known subsystems.
  455. //
  456. RtlLeaveCriticalSection(&SmpKnownSubSysLock);
  457. return KnownSubSys;
  458. }
  459. PSMPKNOWNSUBSYS
  460. SmpLocateKnownSubSysByType(
  461. IN ULONG MuSessionId,
  462. IN ULONG ImageType
  463. )
  464. /*++
  465. Routine Description:
  466. This function scans the known subsystem table looking for
  467. a matching image type.
  468. Arguments:
  469. ImageType - Supplies the image type whose subsystem is to be located.
  470. Return Value:
  471. NULL - The image type does not match a known subsystem.
  472. NON-NULL - Returns the address of the known subsystem.
  473. --*/
  474. {
  475. PSMPKNOWNSUBSYS KnownSubSys = NULL;
  476. PLIST_ENTRY Next;
  477. //
  478. // Aquire known subsystem lock
  479. //
  480. RtlEnterCriticalSection(&SmpKnownSubSysLock);
  481. Next = SmpKnownSubSysHead.Flink;
  482. while ( Next != &SmpKnownSubSysHead ) {
  483. KnownSubSys = CONTAINING_RECORD(Next,SMPKNOWNSUBSYS,Links);
  484. Next = Next->Flink;
  485. if ( (KnownSubSys->ImageType == ImageType) &&
  486. !KnownSubSys->Deleting &&
  487. (KnownSubSys->MuSessionId == MuSessionId) ) {
  488. SmpReferenceKnownSubSys(KnownSubSys);
  489. break;
  490. } else {
  491. KnownSubSys = NULL;
  492. }
  493. }
  494. //
  495. // Unlock known subsystems.
  496. //
  497. RtlLeaveCriticalSection(&SmpKnownSubSysLock);
  498. return KnownSubSys;
  499. }
  500. ENUMSECURITYCONTEXT
  501. SmpClientSecurityContext (
  502. IN PPORT_MESSAGE Message,
  503. IN HANDLE ServerPortHandle
  504. )
  505. /*++
  506. Routine Description:
  507. Impersonate the client and check if it is running under system security context
  508. Arguments:
  509. PPORT_MESSAGE - LPC message pointer
  510. ServerPortHandle - LPC Port Handle
  511. Return Value:
  512. SYSTEM_CONTEXT - Client is running under system LUID
  513. NONSYSTEM_CONTEXT - Failure or client is not running under system LUID
  514. --*/
  515. {
  516. NTSTATUS NtStatus ;
  517. HANDLE ImpersonationToken;
  518. HANDLE TokenHandle;
  519. TOKEN_STATISTICS TokenStatisticsInformation;
  520. ULONG Size;
  521. ENUMSECURITYCONTEXT retval = NONSYSTEM_CONTEXT;
  522. LUID SystemAuthenticationId = SYSTEM_LUID;
  523. NtStatus = NtImpersonateClientOfPort(ServerPortHandle,
  524. Message);
  525. if (!NT_SUCCESS(NtStatus)) {
  526. #if DBG
  527. KdPrint(( "SMSS: NtImpersonateClientOfPort failed: 0x%lX\n",
  528. NtStatus)) ;
  529. #endif
  530. return NONSYSTEM_CONTEXT ;
  531. }
  532. //
  533. // Get the Token Handle.
  534. //
  535. if (NT_SUCCESS(NtOpenThreadToken (NtCurrentThread(),
  536. TOKEN_IMPERSONATE | TOKEN_QUERY,
  537. FALSE,
  538. &TokenHandle) == FALSE)) {
  539. if (NT_SUCCESS(NtQueryInformationToken(
  540. TokenHandle,
  541. TokenStatistics,
  542. &TokenStatisticsInformation,
  543. sizeof(TokenStatisticsInformation),
  544. &Size
  545. ))) {
  546. if ( RtlEqualLuid ( &TokenStatisticsInformation.AuthenticationId,
  547. &SystemAuthenticationId ) ) {
  548. retval = SYSTEM_CONTEXT;
  549. }
  550. }
  551. NtClose(TokenHandle);
  552. } else {
  553. #if DBG
  554. KdPrint(( "SMSS: OpenThreadToken failed\n")) ;
  555. #endif
  556. }
  557. //
  558. //Revert to Self
  559. //
  560. ImpersonationToken = 0;
  561. NtStatus = NtSetInformationThread(NtCurrentThread(),
  562. ThreadImpersonationToken,
  563. &ImpersonationToken,
  564. sizeof(HANDLE));
  565. #if DBG
  566. if (!NT_SUCCESS(NtStatus)) {
  567. KdPrint(( "SMSS: NtSetInformationThread : %lx\n", NtStatus));
  568. }
  569. #endif // DBG
  570. return retval;
  571. }