Leaked source code of windows server 2003
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.

816 lines
23 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. while (1) {
  123. Status = NtReplyPort (ConnectionPort,
  124. (PPORT_MESSAGE) SmApiReplyMsg);
  125. if (Status == STATUS_NO_MEMORY) {
  126. LARGE_INTEGER DelayTime;
  127. KdPrint (("SMSS: Failed to reply to calling thread, retrying.\n"));
  128. DelayTime.QuadPart = Int32x32To64 (5000, -10000);
  129. NtDelayExecution (FALSE, &DelayTime);
  130. continue;
  131. }
  132. break;
  133. }
  134. }
  135. InterlockedDecrement(&SmTotalApiThreads);
  136. RtlSetThreadIsCritical(FALSE, NULL, TRUE);
  137. RtlExitUserThread(STATUS_SUCCESS);
  138. //
  139. // RtlExitUserThread never returns
  140. //
  141. }
  142. } while ( InterlockedCompareExchange(&SmWorkerThreadsAvailable, CapturedThreads + 1, CapturedThreads) != CapturedThreads);
  143. }
  144. if (SmTotalApiThreads == 1) {
  145. SmpFlushDeferredList();
  146. }
  147. while (1) {
  148. Status = NtReplyWaitReceivePort (ConnectionPort,
  149. &ClientContext,
  150. (PPORT_MESSAGE) SmApiReplyMsg,
  151. (PPORT_MESSAGE) SmApiMsg);
  152. //
  153. // If we get an out of memory error then just wait instead of looping
  154. //
  155. if (Status == STATUS_NO_MEMORY) {
  156. LARGE_INTEGER DelayTime;
  157. if (SmApiReplyMsg != NULL) {
  158. KdPrint (("SMSS: Failed to reply to calling thread, retrying.\n"));
  159. }
  160. DelayTime.QuadPart = Int32x32To64 (5000, -10000);
  161. NtDelayExecution (FALSE, &DelayTime);
  162. continue;
  163. }
  164. break;
  165. }
  166. //
  167. // Launching at the same time a several subsystems can deadlock smss
  168. // if it has only two worker threads.
  169. // We create more threads if there is no server thread available
  170. //
  171. if (InterlockedDecrement(&SmWorkerThreadsAvailable) == 0) {
  172. NTSTATUS st = RtlCreateUserThread(
  173. NtCurrentProcess(),
  174. NULL,
  175. FALSE,
  176. 0L,
  177. 0L,
  178. 0L,
  179. SmpApiLoop,
  180. (PVOID) ThreadParameter,
  181. NULL,
  182. NULL
  183. );
  184. }
  185. if (!NT_SUCCESS (Status)) {
  186. SmApiReplyMsg = NULL;
  187. continue;
  188. } else if ( SmApiMsg->h.u2.s2.Type == LPC_CONNECTION_REQUEST ) {
  189. SmpHandleConnectionRequest( ConnectionPort,
  190. (PSBAPIMSG) SmApiMsg
  191. );
  192. SmApiReplyMsg = NULL;
  193. } else if ( SmApiMsg->h.u2.s2.Type == LPC_PORT_CLOSED ) {
  194. if (ClientContext) {
  195. SmpPushDeferredClientContext(ClientContext);
  196. }
  197. SmApiReplyMsg = NULL;
  198. } else {
  199. if ( !ClientContext ) {
  200. SmApiReplyMsg = NULL;
  201. continue;
  202. }
  203. KnownSubSys = ClientContext->KnownSubSys;
  204. SmApiMsg->ReturnedStatus = STATUS_PENDING;
  205. if ((ULONG) SmApiMsg->ApiNumber >= (ULONG) SmMaxApiNumber ) {
  206. Status = STATUS_NOT_IMPLEMENTED;
  207. } else {
  208. switch (SmApiMsg->ApiNumber) {
  209. case SmExecPgmApi :
  210. Status = (SmpApiDispatch[SmApiMsg->ApiNumber])(
  211. SmApiMsg,
  212. ClientContext,
  213. ConnectionPort);
  214. break;
  215. case SmLoadDeferedSubsystemApi :
  216. Status = (SmpApiDispatch[SmApiMsg->ApiNumber])(
  217. SmApiMsg,
  218. ClientContext,
  219. ConnectionPort);
  220. break;
  221. case SmStopCsrApi :
  222. case SmStartCsrApi :
  223. //
  224. // These Api's can only be called from a system process
  225. //
  226. if (ClientContext->SecurityContext == UNKNOWN_CONTEXT) {
  227. //
  228. // Initialize the client security context
  229. //
  230. ClientContext->SecurityContext =
  231. SmpClientSecurityContext ((PPORT_MESSAGE)SmApiMsg,
  232. ClientContext->ServerPortHandle);
  233. }
  234. if (ClientContext->SecurityContext == SYSTEM_CONTEXT) {
  235. Status = (SmpApiDispatch[SmApiMsg->ApiNumber])(
  236. SmApiMsg,
  237. ClientContext,
  238. ConnectionPort);
  239. } else {
  240. #if DBG
  241. KdPrint(("SMSS: Calling Sm Terminal Server Api from Non-System context.Status = STATUS_ACCESS_DENIED\n"));
  242. #endif
  243. Status = STATUS_ACCESS_DENIED;
  244. }
  245. break;
  246. case SmCreateForeignSessionApi :
  247. case SmSessionCompleteApi :
  248. case SmTerminateForeignSessionApi :
  249. if (!KnownSubSys) {
  250. Status = STATUS_INVALID_PARAMETER;
  251. } else {
  252. Status =
  253. (SmpApiDispatch[SmApiMsg->ApiNumber])(
  254. SmApiMsg,
  255. ClientContext,
  256. ConnectionPort);
  257. }
  258. break;
  259. }
  260. }
  261. SmApiMsg->ReturnedStatus = Status;
  262. SmApiReplyMsg = SmApiMsg;
  263. }
  264. }
  265. } except (DbgpUnhandledExceptionFilter( GetExceptionInformation() )) {
  266. ;
  267. }
  268. //
  269. // Make the compiler happy
  270. //
  271. return STATUS_UNSUCCESSFUL;
  272. }
  273. NTSTATUS
  274. SmpHandleConnectionRequest(
  275. IN HANDLE ConnectionPort,
  276. IN PSBAPIMSG Message
  277. )
  278. /*++
  279. Routine Description:
  280. This routine handles connection requests from either known subsystems,
  281. or other clients. Other clients are admin processes.
  282. The protocol for connection from a known subsystem is:
  283. capture the name of the subsystem's Sb API port
  284. Accept the connection
  285. Connect to the subsystems Sb API port
  286. Store the communication port handle in the known subsystem database
  287. signal the event associated with the known subsystem
  288. The protocol for others is to simply validate and accept the connection
  289. request.
  290. Arguments:
  291. Return Value:
  292. None.
  293. --*/
  294. {
  295. NTSTATUS st;
  296. HANDLE CommunicationPort;
  297. REMOTE_PORT_VIEW ClientView;
  298. PSBCONNECTINFO ConnectInfo;
  299. ULONG ConnectInfoLength;
  300. PSMPKNOWNSUBSYS KnownSubSys, KnownSubSys2;
  301. BOOLEAN Accept;
  302. UNICODE_STRING SubSystemPort;
  303. SECURITY_QUALITY_OF_SERVICE DynamicQos;
  304. PSMP_CLIENT_CONTEXT ClientContext;
  305. OBJECT_ATTRIBUTES ObjA;
  306. HANDLE ClientProcessHandle=NULL;
  307. ULONG MuSessionId = 0;
  308. //
  309. // Set up the security quality of service parameters to use over the
  310. // sb API port. Use the most efficient (least overhead) - which is dynamic
  311. // rather than static tracking.
  312. //
  313. DynamicQos.ImpersonationLevel = SecurityIdentification;
  314. DynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
  315. DynamicQos.EffectiveOnly = TRUE;
  316. Accept = TRUE; // Assume we will accept
  317. //
  318. // Get MuSessionId of the client if session manager is not connecting to itself
  319. //
  320. if ( (ULONG_PTR) Message->h.ClientId.UniqueProcess == SmUniqueProcessId ) {
  321. KnownSubSys = NULL;
  322. ClientProcessHandle = NULL;
  323. } else {
  324. InitializeObjectAttributes( &ObjA, NULL, 0, NULL, NULL );
  325. st = NtOpenProcess( &ClientProcessHandle, PROCESS_QUERY_INFORMATION,
  326. &ObjA, &Message->h.ClientId );
  327. if (NT_SUCCESS (st)) {
  328. SmpGetProcessMuSessionId( ClientProcessHandle, &MuSessionId );
  329. } else {
  330. Accept = FALSE;
  331. }
  332. }
  333. ConnectInfo = &Message->ConnectionRequest;
  334. KnownSubSys = SmpLocateKnownSubSysByCid(&Message->h.ClientId);
  335. if ( (KnownSubSys) && (Accept == TRUE) ) {
  336. KnownSubSys2 = SmpLocateKnownSubSysByType(MuSessionId, ConnectInfo->SubsystemImageType);
  337. if (KnownSubSys2 == KnownSubSys ) {
  338. Accept = FALSE;
  339. KdPrint(("SMSS: Connection from SubSystem rejected\n"));
  340. KdPrint(("SMSS: Image type already being served\n"));
  341. } else {
  342. KnownSubSys->ImageType = ConnectInfo->SubsystemImageType;
  343. }
  344. if (KnownSubSys2) {
  345. RtlEnterCriticalSection( &SmpKnownSubSysLock );
  346. SmpDeferenceKnownSubSys(KnownSubSys2);
  347. RtlLeaveCriticalSection( &SmpKnownSubSysLock );
  348. }
  349. }
  350. if (Accept) {
  351. ClientContext = RtlAllocateHeap(SmpHeap, MAKE_TAG( SM_TAG ), sizeof(SMP_CLIENT_CONTEXT));
  352. if ( ClientContext ) {
  353. ClientContext->ClientProcessHandle = ClientProcessHandle;
  354. ClientContext->KnownSubSys = KnownSubSys;
  355. //
  356. // The sm apis used by Terminal Server to start and stop CSR
  357. // do not get called from known subsystems and are restricted
  358. // to system processes only.
  359. //
  360. ClientContext->SecurityContext = UNKNOWN_CONTEXT;
  361. ClientContext->ServerPortHandle = NULL;
  362. } else {
  363. Accept = FALSE;
  364. }
  365. }
  366. ClientView.Length = sizeof(ClientView);
  367. st = NtAcceptConnectPort(
  368. &CommunicationPort,
  369. ClientContext,
  370. (PPORT_MESSAGE)Message,
  371. Accept,
  372. NULL,
  373. &ClientView
  374. );
  375. if ( Accept ) {
  376. if (NT_SUCCESS (st)) {
  377. if (ClientContext) {
  378. ClientContext->ServerPortHandle = CommunicationPort;
  379. }
  380. if ( KnownSubSys ) {
  381. KnownSubSys->SmApiCommunicationPort = CommunicationPort;
  382. }
  383. st = NtCompleteConnectPort(CommunicationPort);
  384. if (!NT_SUCCESS(st)) {
  385. if ( KnownSubSys ) {
  386. KnownSubSys->SmApiCommunicationPort = NULL;
  387. RtlEnterCriticalSection( &SmpKnownSubSysLock );
  388. SmpDeferenceKnownSubSys(KnownSubSys);
  389. RtlLeaveCriticalSection( &SmpKnownSubSysLock );
  390. }
  391. return st;
  392. }
  393. //
  394. // Connect Back to subsystem.
  395. //
  396. if ( KnownSubSys ) {
  397. ConnectInfo->EmulationSubSystemPortName[
  398. sizeof (ConnectInfo->EmulationSubSystemPortName)/sizeof (WCHAR) - 1] = '\0';
  399. RtlCreateUnicodeString( &SubSystemPort,
  400. ConnectInfo->EmulationSubSystemPortName
  401. );
  402. ConnectInfoLength = sizeof( *ConnectInfo );
  403. st = NtConnectPort(
  404. &KnownSubSys->SbApiCommunicationPort,
  405. &SubSystemPort,
  406. &DynamicQos,
  407. NULL,
  408. NULL,
  409. NULL,
  410. NULL,
  411. NULL
  412. );
  413. if ( !NT_SUCCESS(st) ) {
  414. KdPrint(("SMSS: Connect back to Sb %wZ failed %lx\n",&SubSystemPort,st));
  415. }
  416. RtlFreeUnicodeString( &SubSystemPort );
  417. NtSetEvent(KnownSubSys->Active,NULL);
  418. }
  419. } else {
  420. if (ClientProcessHandle) {
  421. NtClose( ClientProcessHandle );
  422. }
  423. RtlFreeHeap( SmpHeap, 0, ClientContext );
  424. }
  425. } else {
  426. if (ClientProcessHandle) {
  427. NtClose( ClientProcessHandle );
  428. }
  429. }
  430. if (KnownSubSys) {
  431. RtlEnterCriticalSection( &SmpKnownSubSysLock );
  432. SmpDeferenceKnownSubSys(KnownSubSys);
  433. RtlLeaveCriticalSection( &SmpKnownSubSysLock );
  434. }
  435. return st;
  436. }
  437. PSMPKNOWNSUBSYS
  438. SmpLocateKnownSubSysByCid(
  439. IN PCLIENT_ID ClientId
  440. )
  441. /*++
  442. Routine Description:
  443. This function scans the known subsystem table looking for
  444. a matching client id (just UniqueProcess portion). If found,
  445. than the connection request is from a known subsystem and
  446. accept is always granted. Otherwise, it must be an administrative
  447. process.
  448. Arguments:
  449. ClientId - Supplies the ClientId whose UniqueProcess field is to be used
  450. in the known subsystem scan.
  451. Return Value:
  452. NULL - The ClientId does not match a known subsystem.
  453. NON-NULL - Returns the address of the known subsystem.
  454. --*/
  455. {
  456. PSMPKNOWNSUBSYS KnownSubSys = NULL;
  457. PLIST_ENTRY Next;
  458. //
  459. // Acquire known subsystem lock.
  460. //
  461. RtlEnterCriticalSection(&SmpKnownSubSysLock);
  462. Next = SmpKnownSubSysHead.Flink;
  463. while ( Next != &SmpKnownSubSysHead ) {
  464. KnownSubSys = CONTAINING_RECORD(Next,SMPKNOWNSUBSYS,Links);
  465. Next = Next->Flink;
  466. if ( (KnownSubSys->InitialClientId.UniqueProcess == ClientId->UniqueProcess) &&
  467. !KnownSubSys->Deleting ) {
  468. SmpReferenceKnownSubSys(KnownSubSys);
  469. break;
  470. } else {
  471. KnownSubSys = NULL;
  472. }
  473. }
  474. //
  475. // Unlock known subsystems.
  476. //
  477. RtlLeaveCriticalSection(&SmpKnownSubSysLock);
  478. return KnownSubSys;
  479. }
  480. PSMPKNOWNSUBSYS
  481. SmpLocateKnownSubSysByType(
  482. IN ULONG MuSessionId,
  483. IN ULONG ImageType
  484. )
  485. /*++
  486. Routine Description:
  487. This function scans the known subsystem table looking for
  488. a matching image type.
  489. Arguments:
  490. ImageType - Supplies the image type whose subsystem is to be located.
  491. Return Value:
  492. NULL - The image type does not match a known subsystem.
  493. NON-NULL - Returns the address of the known subsystem.
  494. --*/
  495. {
  496. PSMPKNOWNSUBSYS KnownSubSys = NULL;
  497. PLIST_ENTRY Next;
  498. //
  499. // Aquire known subsystem lock
  500. //
  501. RtlEnterCriticalSection(&SmpKnownSubSysLock);
  502. Next = SmpKnownSubSysHead.Flink;
  503. while ( Next != &SmpKnownSubSysHead ) {
  504. KnownSubSys = CONTAINING_RECORD(Next,SMPKNOWNSUBSYS,Links);
  505. Next = Next->Flink;
  506. if ( (KnownSubSys->ImageType == ImageType) &&
  507. !KnownSubSys->Deleting &&
  508. (KnownSubSys->MuSessionId == MuSessionId) ) {
  509. SmpReferenceKnownSubSys(KnownSubSys);
  510. break;
  511. } else {
  512. KnownSubSys = NULL;
  513. }
  514. }
  515. //
  516. // Unlock known subsystems.
  517. //
  518. RtlLeaveCriticalSection(&SmpKnownSubSysLock);
  519. return KnownSubSys;
  520. }
  521. ENUMSECURITYCONTEXT
  522. SmpClientSecurityContext (
  523. IN PPORT_MESSAGE Message,
  524. IN HANDLE ServerPortHandle
  525. )
  526. /*++
  527. Routine Description:
  528. Impersonate the client and check if it is running under system security context
  529. Arguments:
  530. PPORT_MESSAGE - LPC message pointer
  531. ServerPortHandle - LPC Port Handle
  532. Return Value:
  533. SYSTEM_CONTEXT - Client is running under system LUID
  534. NONSYSTEM_CONTEXT - Failure or client is not running under system LUID
  535. --*/
  536. {
  537. NTSTATUS NtStatus ;
  538. HANDLE ImpersonationToken;
  539. HANDLE TokenHandle;
  540. TOKEN_STATISTICS TokenStatisticsInformation;
  541. ULONG Size;
  542. ENUMSECURITYCONTEXT retval = NONSYSTEM_CONTEXT;
  543. LUID SystemAuthenticationId = SYSTEM_LUID;
  544. NtStatus = NtImpersonateClientOfPort(ServerPortHandle,
  545. Message);
  546. if (!NT_SUCCESS(NtStatus)) {
  547. #if DBG
  548. KdPrint(( "SMSS: NtImpersonateClientOfPort failed: 0x%lX\n",
  549. NtStatus)) ;
  550. #endif
  551. return NONSYSTEM_CONTEXT ;
  552. }
  553. //
  554. // Get the Token Handle.
  555. //
  556. if (NT_SUCCESS(NtOpenThreadToken (NtCurrentThread(),
  557. TOKEN_IMPERSONATE | TOKEN_QUERY,
  558. FALSE,
  559. &TokenHandle) == FALSE)) {
  560. if (NT_SUCCESS(NtQueryInformationToken(
  561. TokenHandle,
  562. TokenStatistics,
  563. &TokenStatisticsInformation,
  564. sizeof(TokenStatisticsInformation),
  565. &Size
  566. ))) {
  567. if ( RtlEqualLuid ( &TokenStatisticsInformation.AuthenticationId,
  568. &SystemAuthenticationId ) ) {
  569. retval = SYSTEM_CONTEXT;
  570. }
  571. }
  572. NtClose(TokenHandle);
  573. } else {
  574. #if DBG
  575. KdPrint(( "SMSS: OpenThreadToken failed\n")) ;
  576. #endif
  577. }
  578. //
  579. //Revert to Self
  580. //
  581. ImpersonationToken = 0;
  582. NtStatus = NtSetInformationThread(NtCurrentThread(),
  583. ThreadImpersonationToken,
  584. &ImpersonationToken,
  585. sizeof(HANDLE));
  586. #if DBG
  587. if (!NT_SUCCESS(NtStatus)) {
  588. KdPrint(( "SMSS: NtSetInformationThread : %lx\n", NtStatus));
  589. }
  590. #endif // DBG
  591. return retval;
  592. }