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.

9979 lines
316 KiB

  1. //****************************************************************************/
  2. // winsta.c
  3. //
  4. // TermSrv session and session stack related code.
  5. //
  6. // Copyright (C) 1997-2000 Microsoft Corporation
  7. /****************************************************************************/
  8. #include "precomp.h"
  9. #pragma hdrstop
  10. #include "icaevent.h"
  11. #include "tsappcmp.h" // for TermsrvAppInstallMode
  12. #include <msaudite.h>
  13. #include "sessdir.h"
  14. #include <allproc.h>
  15. #include <userenv.h>
  16. #include <winsock2.h>
  17. #include "conntfy.h"
  18. #include "tsremdsk.h"
  19. #include <ws2tcpip.h>
  20. #include <Accctrl.h>
  21. #include <Aclapi.h>
  22. #include "tssec.h"
  23. //
  24. // Autoreconnect security headers
  25. //
  26. #include <md5.h>
  27. #include <hmac.h>
  28. // performance flags
  29. #include "tsperf.h"
  30. #ifndef MAX_WORD
  31. #define MAX_WORD 0xffff
  32. #endif
  33. //
  34. // SIGN_BYPASS_OPTION #define should be removed before WIN64 SHIPS!!!!!
  35. //
  36. #ifdef _WIN64
  37. #define SIGN_BYPASS_OPTION
  38. #endif
  39. /*
  40. * Local defines
  41. */
  42. #define SETUP_REG_PATH L"\\Registry\\Machine\\System\\Setup"
  43. #define REG_WINDOWS_KEY TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion")
  44. #define MAXIMUM_WAIT_WINSTATIONS ((MAXIMUM_WAIT_OBJECTS >> 1) - 1)
  45. #define MAX_STRING_BYTES 512
  46. BOOL gbFirtsConnectionThread = TRUE;
  47. WINSTATIONCONFIG2 gConsoleConfig;
  48. WCHAR g_DigProductId[CLIENT_PRODUCT_ID_LENGTH];
  49. RECONNECT_INFO ConsoleReconnectInfo;
  50. ULONG gLogoffTimeout = 90; /*90 seconds default value for logoff timeout*/
  51. /*
  52. * Globals to support load balancing. Since this is queried frequently we can't
  53. * afford to lock the winstation list and count them up. Note that they are
  54. * modified only when we have the WinStationListLock to avoid mutual exclusion
  55. * issues.
  56. */
  57. ULONG WinStationTotalCount = 0;
  58. ULONG WinStationDiscCount = 0;
  59. LOAD_BALANCING_METRICS gLB;
  60. /*
  61. * External procedures defined
  62. */
  63. VOID StartAllWinStations(HKEY);
  64. NTSTATUS QueueWinStationCreate( PWINSTATIONNAME );
  65. NTSTATUS WinStationCreateWorker(PWINSTATIONNAME pWinStationName, PULONG pLogonId );
  66. VOID WinStationTerminate( PWINSTATION );
  67. VOID WinStationDeleteWorker( PWINSTATION );
  68. NTSTATUS WinStationDoDisconnect( PWINSTATION, PRECONNECT_INFO, BOOLEAN );
  69. NTSTATUS WinStationDoReconnect( PWINSTATION, PRECONNECT_INFO );
  70. BOOL CopyReconnectInfo(PWINSTATION, PRECONNECT_INFO);
  71. VOID CleanupReconnect( PRECONNECT_INFO );
  72. NTSTATUS WinStationExceptionFilter( PWSTR, PEXCEPTION_POINTERS );
  73. NTSTATUS IcaWinStationNameFromLogonId( ULONG, PWINSTATIONNAME );
  74. VOID WriteErrorLogEntry(
  75. IN NTSTATUS NtStatusCode,
  76. IN PVOID pRawData,
  77. IN ULONG RawDataLength
  78. );
  79. NTSTATUS CheckIdleWinstation(VOID);
  80. BOOL IsKernelDebuggerAttached();
  81. /*
  82. * Internal procedures defined
  83. */
  84. NTSTATUS WinStationTerminateThread( PVOID );
  85. NTSTATUS WinStationIdleControlThread( PVOID );
  86. NTSTATUS WinStationConnectThread( ULONG );
  87. NTSTATUS WinStationTransferThread( PVOID );
  88. NTSTATUS ConnectSmWinStationApiPort( VOID );
  89. NTSTATUS IcaRegWinStationEnumerate( PULONG, PWINSTATIONNAME, PULONG );
  90. NTSTATUS WinStationStart( PWINSTATION );
  91. BOOL WinStationTerminateProcesses( PWINSTATION, ULONG *pNumTerminated );
  92. VOID WinStationDeleteProc( PREFLOCK );
  93. VOID WinStationZombieProc( PREFLOCK );
  94. NTSTATUS SetRefLockDeleteProc( PREFLOCK, PREFLOCKDELETEPROCEDURE );
  95. VOID WsxBrokenConnection( PWINSTATION );
  96. NTSTATUS TerminateProcessAndWait( HANDLE, HANDLE, ULONG );
  97. VOID ResetAutoReconnectInfo( PWINSTATION );
  98. ULONG WinStationShutdownReset( PVOID );
  99. ULONG WinStationLogoff( PVOID );
  100. NTSTATUS DoForWinStationGroup( PULONG, ULONG, LPTHREAD_START_ROUTINE );
  101. NTSTATUS LogoffWinStation( PWINSTATION, ULONG );
  102. PWINSTATION FindIdleWinStation( VOID );
  103. ULONG CountWinStationType(
  104. PWINSTATIONNAME pListenName,
  105. BOOLEAN bActiveOnly,
  106. BOOLEAN bLockHeld);
  107. NTSTATUS
  108. _CloseEndpoint(
  109. IN PWINSTATIONCONFIG2 pWinStationConfig,
  110. IN PVOID pEndpoint,
  111. IN ULONG EndpointLength,
  112. IN PWINSTATION pWinStation,
  113. IN BOOLEAN bNeedStack
  114. );
  115. NTSTATUS _VerifyStackModules(PWINSTATION);
  116. NTSTATUS _ImpersonateClient(HANDLE, HANDLE *);
  117. WinstationRegUnLoadKey(HKEY hKey, LPWSTR lpSubKey);
  118. ULONG WinstationCountUserSessions(PSID, ULONG);
  119. BOOLEAN WinStationCheckConsoleSession(VOID);
  120. NTSTATUS
  121. WinStationWinerrorToNtStatus(ULONG ulWinError);
  122. VOID
  123. WinStationSetMaxOustandingConnections();
  124. BOOL IsClientOnSameMachine(PWINSTATION pWinStation);
  125. NTSTATUS GetProductIdFromRegistry( WCHAR* DigProductId, DWORD dwSize );
  126. /*
  127. * External procedures used
  128. */
  129. NTSTATUS WinStationInitRPC( VOID );
  130. NTSTATUS WinStationInitLPC( VOID );
  131. NTSTATUS WinStationStopAllShadows( PWINSTATION );
  132. VOID NotifySystemEvent( ULONG );
  133. NTSTATUS SendWinStationCommand( PWINSTATION, PWINSTATION_APIMSG, ULONG );
  134. NTSTATUS RpcCheckClientAccess( PWINSTATION, ACCESS_MASK, BOOLEAN );
  135. NTSTATUS WinStationSecurityInit( VOID );
  136. VOID DisconnectTimeout( ULONG LogonId );
  137. PWSEXTENSION FindWinStationExtensionDll( PWSTR, ULONG );
  138. PSECURITY_DESCRIPTOR
  139. WinStationGetSecurityDescriptor(
  140. PWINSTATION pWinStation
  141. );
  142. VOID
  143. WinStationFreeSecurityDescriptor(
  144. PWINSTATION pWinStation
  145. );
  146. NTSTATUS
  147. WinStationInheritSecurityDescriptor(
  148. PVOID pSecurityDescriptor,
  149. PWINSTATION pTargetWinStation
  150. );
  151. NTSTATUS
  152. ReadWinStationSecurityDescriptor(
  153. PWINSTATION pWinStation
  154. );
  155. NTSTATUS
  156. WinStationReadRegistryWorker();
  157. void
  158. PostErrorValueEvent(
  159. unsigned EventCode, DWORD ErrVal);
  160. BOOL
  161. Filter_AddOutstandingConnection(
  162. IN HANDLE pContext,
  163. IN PVOID pEndpoint,
  164. IN ULONG EndpointLength,
  165. OUT PBYTE pin_addr,
  166. OUT PUINT puAddrSize,
  167. OUT BOOLEAN *pbBlocked
  168. );
  169. BOOL
  170. Filter_RemoveOutstandingConnection(
  171. IN PBYTE pin_addr,
  172. IN UINT uAddrSize
  173. );
  174. RTL_GENERIC_COMPARE_RESULTS
  175. NTAPI
  176. Filter_CompareConnectionEntry(
  177. IN struct _RTL_GENERIC_TABLE *Table,
  178. IN PVOID FirstInstance,
  179. IN PVOID SecondInstance
  180. );
  181. PVOID
  182. Filter_AllocateConnectionEntry(
  183. IN struct _RTL_GENERIC_TABLE *Table,
  184. IN CLONG ByteSize
  185. );
  186. PVOID
  187. Filter_AllocateConnectionEntry(
  188. IN struct _RTL_GENERIC_TABLE *Table,
  189. IN CLONG ByteSize
  190. );
  191. VOID
  192. Filter_FreeConnectionEntry (
  193. IN struct _RTL_GENERIC_TABLE *Table,
  194. IN PVOID Buffer
  195. );
  196. BOOL
  197. FindFirstListeningWinStationName(
  198. PWINSTATIONNAMEW pListenName,
  199. PWINSTATIONCONFIG2 pConfig );
  200. typedef struct _TRANSFER_INFO {
  201. ULONG LogonId;
  202. PVOID pEndpoint;
  203. ULONG EndpointLength;
  204. } TRANSFER_INFO, *PTRANSFER_INFO;
  205. VOID AuditEvent( PWINSTATION pWinstation, ULONG EventId );
  206. VOID AuditShutdownEvent(VOID);
  207. /*
  208. * Local variables
  209. */
  210. RTL_CRITICAL_SECTION WinStationListLock;
  211. RTL_CRITICAL_SECTION WinStationListenersLock;
  212. RTL_CRITICAL_SECTION WinStationStartCsrLock;
  213. RTL_CRITICAL_SECTION TimerCritSec;
  214. RTL_CRITICAL_SECTION WinStationZombieLock;
  215. RTL_CRITICAL_SECTION UserProfileLock;
  216. RTL_CRITICAL_SECTION ConsoleLock;
  217. RTL_RESOURCE WinStationSecurityLock;
  218. // This synchronization counter prevents WinStationIdleControlThread from
  219. // Trying to create a console session when there is not one. There are two
  220. // Situations where we do no want it to create such session:
  221. // - At initialization time before we create session Zero which is the initial
  222. // console session.
  223. // - During reconnect in the window were we just disconnected the console session
  224. // (so there is no console session) but we know we are we going to reconnect
  225. // an other session to the console.
  226. ULONG gConsoleCreationDisable = 1;
  227. LIST_ENTRY WinStationListHead; // protected by WinStationListLock
  228. LIST_ENTRY SystemEventHead; // protected by WinStationListLock
  229. LIST_ENTRY ZombieListHead;
  230. ULONG LogonId;
  231. LARGE_INTEGER TimeoutZero;
  232. HANDLE WinStationEvent = NULL;
  233. HANDLE WinStationIdleControlEvent = NULL;
  234. HANDLE ConsoleLogoffEvent = NULL;
  235. HANDLE g_hMachineGPEvent=NULL;
  236. static HANDLE WinStationApiPort = NULL;
  237. BOOLEAN StopOnDown = FALSE;
  238. HANDLE hTrace = NULL;
  239. //BOOLEAN ShutdownTerminateNoWait = FALSE;
  240. ULONG ShutDownFromSessionID = 0;
  241. ULONG IdleWinStationPoolCount = 2;
  242. ULONG_PTR gMinPerSessionPageCommitMB = 20;
  243. #define REG_MIN_PERSESSION_PAGECOMMIT L"MinPerSessionPageCommit"
  244. PVOID glpAddress;
  245. ULONG_PTR gMinPerSessionPageCommit;
  246. typedef struct _TS_OUTSTANDINGCONNECTION {
  247. ULONGLONG blockUntilTime;
  248. ULONG NumOutStandingConnect;
  249. UINT uAddrSize;
  250. BYTE addr[16];
  251. struct _TS_OUTSTANDINGCONNECTION *pNext;
  252. } TS_OUTSTANDINGCONNECTION, *PTS_OUTSTANDINGCONNECTION;
  253. PTS_OUTSTANDINGCONNECTION g_pBlockedConnections = NULL;
  254. RTL_GENERIC_TABLE gOutStandingConnections;
  255. RTL_CRITICAL_SECTION FilterLock;
  256. ULONG MaxOutStandingConnect;
  257. ULONG NumOutStandingConnect;
  258. ULONG MaxSingleOutStandingConnect; // maximum number of outstanding connections from a single IP
  259. ULONG DelayConnectionTime = 30*1000;
  260. SYSTEMTIME LastLoggedDelayConnection;
  261. ULONGLONG LastLoggedBlockedConnection = 0;
  262. BOOLEAN gbNeverLoggedDelayConnection = TRUE;
  263. HANDLE hConnectEvent;
  264. BOOLEAN gbWinSockInitialized = FALSE;
  265. /*
  266. * Global data
  267. */
  268. extern BOOL g_fAppCompat;
  269. extern BOOL g_SafeBootWithNetwork;
  270. RTL_CRITICAL_SECTION g_AuthzCritSection;
  271. extern HANDLE gReadyEventHandle;
  272. extern BOOLEAN RegDenyTSConnectionsPolicy();
  273. extern DWORD WaitForTSConnectionsPolicyChanges( BOOLEAN bWaitForAccept, HANDLE hEvent );
  274. extern void InitializeConsoleClientData( PWINSTATIONCLIENTW pWC );
  275. // defines in REGAPI
  276. extern BOOLEAN RegGetMachinePolicyEx(
  277. BOOLEAN forcePolicyRead,
  278. FILETIME *pTime ,
  279. PPOLICY_TS_MACHINE pPolicy );
  280. // Global TermSrv counter values
  281. DWORD g_TermSrvTotalSessions;
  282. DWORD g_TermSrvDiscSessions;
  283. DWORD g_TermSrvReconSessions;
  284. // Global system SID
  285. PSID gSystemSid = NULL;
  286. PSID gAdminSid = NULL;
  287. BOOLEAN g_fDenyTSConnectionsPolicy = 0;
  288. POLICY_TS_MACHINE g_MachinePolicy;
  289. /****************************************************************************/
  290. // IsEmbedded
  291. //
  292. // Service-load-time initialization.
  293. /****************************************************************************/
  294. BOOL IsEmbedded()
  295. {
  296. static int fResult = -1;
  297. if(fResult == -1)
  298. {
  299. OSVERSIONINFOEX ovix;
  300. BOOL b;
  301. fResult = 0;
  302. ovix.dwOSVersionInfoSize = sizeof(ovix);
  303. b = GetVersionEx((LPOSVERSIONINFO) &ovix);
  304. ASSERT(b);
  305. if(b && (ovix.wSuiteMask & VER_SUITE_EMBEDDEDNT))
  306. {
  307. fResult = 1;
  308. }
  309. }
  310. return (fResult == 1);
  311. }
  312. /****************************************************************************/
  313. // InitTermSrv
  314. //
  315. // Service-load-time initialization.
  316. /****************************************************************************/
  317. NTSTATUS InitTermSrv(HKEY hKeyTermSrv)
  318. {
  319. NTSTATUS Status;
  320. DWORD dwLen;
  321. DWORD dwType;
  322. ULONG szBuffer[MAX_PATH/sizeof(ULONG)];
  323. FILETIME policyTime;
  324. WSADATA wsaData;
  325. #define MAX_DEFAULT_CONNECTIONS 50
  326. #define MAX_CONNECT_LOW_THRESHOLD 5
  327. #define MAX_SINGLE_CONNECT_THRESHOLD_DIFF 5
  328. #define MAX_DEFAULT_CONNECTIONS_PRO 3
  329. #define MAX_DEFAULT_SINGLE_CONNECTIONS_PRO 2
  330. ASSERT(hKeyTermSrv != NULL);
  331. g_TermSrvTotalSessions = 0;
  332. g_TermSrvDiscSessions = 0;
  333. g_TermSrvReconSessions = 0;
  334. // Set default value for maximum simultaneous connection attempts
  335. WinStationSetMaxOustandingConnections();
  336. NumOutStandingConnect = 0;
  337. hConnectEvent = NULL;
  338. ShutdownInProgress = FALSE;
  339. //ShutdownTerminateNoWait = FALSE;
  340. ShutDownFromSessionID = 0;
  341. // don't bother saving the policy time, the thread that waits for policy update will save it's own copy at the
  342. // cost of running the 1st time around. Alternatively, I need to use another global var for the policy update value.
  343. RegGetMachinePolicyEx( TRUE, &policyTime, &g_MachinePolicy );
  344. Status = RtlInitializeCriticalSection( &FilterLock );
  345. ASSERT( NT_SUCCESS( Status ));
  346. if (!NT_SUCCESS(Status)) {
  347. goto badFilterLock;
  348. }
  349. RtlInitializeGenericTable( &gOutStandingConnections,
  350. Filter_CompareConnectionEntry,
  351. Filter_AllocateConnectionEntry,
  352. Filter_FreeConnectionEntry,
  353. NULL );
  354. Status = RtlInitializeCriticalSection( &ConsoleLock );
  355. ASSERT( NT_SUCCESS( Status ) );
  356. if (!NT_SUCCESS(Status)) {
  357. goto badConsoleLock;
  358. }
  359. Status = RtlInitializeCriticalSection( &UserProfileLock );
  360. ASSERT( NT_SUCCESS( Status ) );
  361. if (!NT_SUCCESS(Status)) {
  362. goto badUserProfileLock;
  363. }
  364. Status = RtlInitializeCriticalSection( &WinStationListLock );
  365. ASSERT( NT_SUCCESS( Status ) );
  366. if (!NT_SUCCESS(Status)) {
  367. goto badWinstationListLock;
  368. }
  369. if (!gbServer) {
  370. Status = RtlInitializeCriticalSection( &WinStationListenersLock );
  371. ASSERT( NT_SUCCESS( Status ) );
  372. if (!NT_SUCCESS(Status)) {
  373. goto badWinStationListenersLock;
  374. }
  375. }
  376. Status = RtlInitializeCriticalSection( &WinStationZombieLock );
  377. ASSERT( NT_SUCCESS( Status ) );
  378. if (!NT_SUCCESS(Status)) {
  379. goto badWinStationZombieLock;
  380. }
  381. Status = RtlInitializeCriticalSection( &TimerCritSec );
  382. ASSERT( NT_SUCCESS( Status ) );
  383. if (!NT_SUCCESS(Status)) {
  384. goto badTimerCritsec;
  385. }
  386. Status = RtlInitializeCriticalSection( &g_AuthzCritSection );
  387. ASSERT( NT_SUCCESS( Status ) );
  388. if (!NT_SUCCESS(Status)) {
  389. goto badAuthzCritSection;
  390. }
  391. InitializeListHead( &WinStationListHead );
  392. InitializeListHead( &SystemEventHead );
  393. InitializeListHead( &ZombieListHead );
  394. Status = InitializeConsoleNotification ();
  395. if (!NT_SUCCESS(Status)) {
  396. goto badinitStartCsrLock;
  397. }
  398. Status = RtlInitializeCriticalSection( &WinStationStartCsrLock );
  399. ASSERT( NT_SUCCESS( Status ) );
  400. if (!NT_SUCCESS(Status)) {
  401. goto badinitStartCsrLock;
  402. }
  403. Status = LCInitialize(
  404. g_bPersonalTS ? LC_INIT_LIMITED : LC_INIT_ALL,
  405. g_fAppCompat
  406. );
  407. if (!NT_SUCCESS(Status)) {
  408. goto badLcInit;
  409. }
  410. //
  411. // Listener winstations always get LogonId above 65536 and are
  412. // assigned by Terminal Server. LogonId's for sessions are
  413. // generated by mm in the range 0-65535
  414. //
  415. LogonId = MAX_WORD + 1;
  416. TimeoutZero = RtlConvertLongToLargeInteger( 0 );
  417. Status = NtCreateEvent( &WinStationEvent, EVENT_ALL_ACCESS, NULL,
  418. NotificationEvent, FALSE );
  419. Status = NtCreateEvent( &WinStationIdleControlEvent, EVENT_ALL_ACCESS, NULL,
  420. SynchronizationEvent, FALSE );
  421. ASSERT( NT_SUCCESS( Status ) );
  422. if (!NT_SUCCESS(Status)) {
  423. goto badEvent;
  424. }
  425. Status = NtCreateEvent( &ConsoleLogoffEvent, EVENT_ALL_ACCESS, NULL,
  426. NotificationEvent, TRUE );
  427. ASSERT( NT_SUCCESS( Status ) );
  428. if (!NT_SUCCESS(Status)) {
  429. goto badEvent;
  430. }
  431. /*
  432. * Initialize WinStation security
  433. */
  434. RtlAcquireResourceExclusive(&WinStationSecurityLock, TRUE);
  435. Status = WinStationSecurityInit();
  436. RtlReleaseResource(&WinStationSecurityLock);
  437. ASSERT( NT_SUCCESS( Status ) );
  438. if (!NT_SUCCESS(Status)) {
  439. goto badInitSecurity;
  440. }
  441. Status = WinStationInitLPC();
  442. ASSERT( NT_SUCCESS( Status ) );
  443. if (!NT_SUCCESS(Status)) {
  444. goto badInitLPC;
  445. }
  446. //
  447. // Read the registry to determine if maximum outstanding connections
  448. // policy is turned on and the value for it
  449. //
  450. //
  451. // Get MaxOutstandingCon string value
  452. //
  453. dwLen = sizeof(MaxOutStandingConnect);
  454. if (RegQueryValueEx(hKeyTermSrv, MAX_OUTSTD_CONNECT, NULL, &dwType,
  455. (PCHAR)&szBuffer, &dwLen) == ERROR_SUCCESS) {
  456. if (*(PULONG)szBuffer > 0) {
  457. MaxOutStandingConnect = *(PULONG)szBuffer;
  458. }
  459. }
  460. dwLen = sizeof(MaxSingleOutStandingConnect);
  461. if (RegQueryValueEx(hKeyTermSrv, MAX_SINGLE_OUTSTD_CONNECT, NULL, &dwType,
  462. (PCHAR)&szBuffer, &dwLen) == ERROR_SUCCESS) {
  463. if (*(PULONG)szBuffer > 0) {
  464. MaxSingleOutStandingConnect = *(PULONG)szBuffer;
  465. }
  466. }
  467. dwLen = sizeof(gLogoffTimeout);
  468. if (RegQueryValueEx(hKeyTermSrv, LOGOFF_TIMEOUT, NULL, &dwType,
  469. (PCHAR)&szBuffer, &dwLen) == ERROR_SUCCESS) {
  470. gLogoffTimeout = *(PULONG)szBuffer;
  471. }
  472. //
  473. // Read the logoff timeout value. This timeout is used by termsrv to force terminate
  474. // winlogon, if winlogon does not complete logoff after ExitWindows message was sent to him
  475. //
  476. //
  477. // set max number of outstanding connection from single IP
  478. //
  479. if ( MaxOutStandingConnect < MAX_SINGLE_CONNECT_THRESHOLD_DIFF*5)
  480. {
  481. MaxSingleOutStandingConnect = MaxOutStandingConnect - 1;
  482. } else {
  483. MaxSingleOutStandingConnect = MaxOutStandingConnect - MAX_SINGLE_CONNECT_THRESHOLD_DIFF;
  484. }
  485. //
  486. // Create the connect Event
  487. //
  488. if (MaxOutStandingConnect != 0) {
  489. hConnectEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  490. if (hConnectEvent == NULL) {
  491. MaxOutStandingConnect = 0;
  492. }
  493. }
  494. //
  495. // Initialize winsock
  496. //
  497. // Ask for Winsock version 2.2.
  498. if (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0) {
  499. gbWinSockInitialized = TRUE;
  500. }
  501. return(Status);
  502. /*
  503. * Clean up on failure. Clean up is not implemented for
  504. * all cases of failure. However most of it will be done implicitly
  505. * by the exit from termsrv process. Failure at this point means anyway
  506. * there will be no multi-user feature.
  507. */
  508. badInitLPC: // Cleanup code not implemented
  509. badInitSecurity:
  510. badEvent:
  511. if (WinStationEvent != NULL)
  512. NtClose(WinStationEvent);
  513. if (WinStationIdleControlEvent != NULL)
  514. NtClose(WinStationIdleControlEvent);
  515. if (ConsoleLogoffEvent != NULL)
  516. NtClose(ConsoleLogoffEvent);
  517. badLcInit:
  518. RtlDeleteCriticalSection( &WinStationStartCsrLock );
  519. badinitStartCsrLock:
  520. RtlDeleteCriticalSection( &TimerCritSec );
  521. badTimerCritsec:
  522. badWinStationZombieLock:
  523. if (!gbServer) {
  524. RtlDeleteCriticalSection( &WinStationListenersLock );
  525. }
  526. badWinStationListenersLock:
  527. RtlDeleteCriticalSection( &WinStationListLock );
  528. badWinstationListLock:
  529. RtlDeleteCriticalSection( &UserProfileLock );
  530. badUserProfileLock:
  531. RtlDeleteCriticalSection( &ConsoleLock );
  532. badAuthzCritSection:
  533. RtlDeleteCriticalSection( &g_AuthzCritSection );
  534. badConsoleLock:
  535. RtlDeleteCriticalSection( &FilterLock );
  536. badFilterLock:
  537. return Status;
  538. }
  539. /*******************************************************************************
  540. *
  541. * GroupPolicyNotifyThread
  542. * Entry:
  543. * nothing
  544. *
  545. *
  546. *******************************************************************************/
  547. DWORD GroupPolicyNotifyThread(DWORD notUsed )
  548. {
  549. DWORD dwError;
  550. BOOL rc;
  551. HANDLE hEvent;
  552. BOOLEAN bWaitForAccept;
  553. BOOLEAN bSystemStartup;
  554. static FILETIME timeOfLastPolicyRead = { 0 , 0 } ;
  555. rc = RegisterGPNotification( g_hMachineGPEvent, TRUE);
  556. if (rc) {
  557. hEvent = g_hMachineGPEvent;
  558. } else {
  559. // TS can still run with the default set of config data, besides
  560. // if there were any machine group policy data, TS got them on the
  561. // last reboot cycle.
  562. //
  563. hEvent = NULL;
  564. }
  565. //
  566. // At the beginning the listeners are not started.
  567. // So wait (or test) for the connections to be accepted.
  568. //
  569. bWaitForAccept = TRUE;
  570. bSystemStartup = TRUE;
  571. //
  572. // Query and set the global flag before entering any wait.
  573. //
  574. g_fDenyTSConnectionsPolicy = RegDenyTSConnectionsPolicy();
  575. while (TRUE) {
  576. dwError = WaitForTSConnectionsPolicyChanges( bWaitForAccept, hEvent );
  577. //
  578. // Both GP changes and reg changes can affect this one.
  579. //
  580. g_fDenyTSConnectionsPolicy = RegDenyTSConnectionsPolicy();
  581. if (dwError == WAIT_OBJECT_0) {
  582. //
  583. // A change in the TS connections policy has occurred.
  584. //
  585. if (bWaitForAccept) {
  586. // are the connections really accepted?
  587. if (!(g_fDenyTSConnectionsPolicy &&
  588. !(TSIsMachinePolicyAllowHelp() && TSIsMachineInHelpMode()))) {
  589. // Start the listeners.
  590. if ( bSystemStartup ) {
  591. // the first time, start all listeners
  592. StartStopListeners(NULL, TRUE);
  593. } else {
  594. // after the first time, use this function to start
  595. // listeners only as needed.
  596. WinStationReadRegistryWorker();
  597. }
  598. // Switch to a wait for denied connections.
  599. bWaitForAccept = FALSE;
  600. bSystemStartup = FALSE;
  601. }
  602. } else {
  603. // are the connections really denied?
  604. if (g_fDenyTSConnectionsPolicy &&
  605. !(TSIsMachinePolicyAllowHelp() && TSIsMachineInHelpMode())) {
  606. // Stop the listeners.
  607. StartStopListeners(NULL, FALSE);
  608. // Switch to a wait for accepted connections.
  609. bWaitForAccept = TRUE;
  610. }
  611. }
  612. } else if (dwError == WAIT_OBJECT_0 + 1) {
  613. //
  614. // We got notified that the GP has changed.
  615. //
  616. if ( RegGetMachinePolicyEx( FALSE, & timeOfLastPolicyRead, &g_MachinePolicy ) )
  617. {
  618. // there has been a change, go ahead with the actual updates
  619. WinStationReadRegistryWorker();
  620. // Also update the session directory settings if on an app
  621. // server.
  622. if (!g_bPersonalTS && g_fAppCompat) {
  623. UpdateSessionDirectory();
  624. }
  625. }
  626. } else {
  627. // should we not do something else?
  628. Sleep( 5000 );
  629. continue;
  630. }
  631. }
  632. if (rc) {
  633. UnregisterGPNotification(g_hMachineGPEvent);
  634. }
  635. return 0;
  636. }
  637. /*******************************************************************************
  638. *
  639. * StartAllWinStations
  640. *
  641. * Get list of configured WinStations from the registry,
  642. * start the Console, and then start all remaining WinStations.
  643. *
  644. * ENTRY:
  645. * nothing
  646. *
  647. * EXIT:
  648. * nothing
  649. *
  650. ******************************************************************************/
  651. void StartAllWinStations(HKEY hKeyTermSrv)
  652. {
  653. OBJECT_ATTRIBUTES ObjectAttributes;
  654. UNICODE_STRING KeyPath;
  655. HANDLE KeyHandle;
  656. UNICODE_STRING ValueName;
  657. #define VALUE_BUFFER_SIZE (sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 256 * sizeof(WCHAR))
  658. CHAR ValueBuffer[VALUE_BUFFER_SIZE];
  659. PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo;
  660. ULONG ValueLength;
  661. DWORD ThreadId;
  662. NTSTATUS Status;
  663. DWORD ValueType;
  664. DWORD ValueSize;
  665. #define AUTOSTARTTIME 600000
  666. ASSERT(hKeyTermSrv != NULL);
  667. /*
  668. * Initialize the number of idle winstations and gMinPerSessionPageCommitMB,
  669. * if this value is in the registry.
  670. */
  671. ValueSize = sizeof(IdleWinStationPoolCount);
  672. Status = RegQueryValueEx(hKeyTermSrv,
  673. REG_CITRIX_IDLEWINSTATIONPOOLCOUNT,
  674. NULL,
  675. &ValueType,
  676. (LPBYTE) &ValueBuffer,
  677. &ValueSize );
  678. if ( Status == ERROR_SUCCESS ) {
  679. IdleWinStationPoolCount = *(PULONG)ValueBuffer;
  680. }
  681. //get the product id from registry for use in detecting shadow loop
  682. GetProductIdFromRegistry( g_DigProductId, sizeof( g_DigProductId ) );
  683. //Terminal Service needs to skip Memory check in Embedded images
  684. //when the TS service starts
  685. //bug #246972
  686. if(!IsEmbedded()) {
  687. ValueSize = sizeof(gMinPerSessionPageCommitMB);
  688. Status = RegQueryValueEx(hKeyTermSrv,
  689. REG_MIN_PERSESSION_PAGECOMMIT,
  690. NULL,
  691. &ValueType,
  692. (LPBYTE) &ValueBuffer,
  693. &ValueSize );
  694. if ( Status == ERROR_SUCCESS ) {
  695. gMinPerSessionPageCommitMB = *(PULONG)ValueBuffer;
  696. }
  697. gMinPerSessionPageCommit = gMinPerSessionPageCommitMB * 1024 * 1024;
  698. Status = NtAllocateVirtualMemory( NtCurrentProcess(),
  699. &glpAddress,
  700. 0,
  701. &gMinPerSessionPageCommit,
  702. MEM_RESERVE,
  703. PAGE_READWRITE
  704. );
  705. ASSERT( NT_SUCCESS( Status ) );
  706. }
  707. /*
  708. * Open connection to our WinStationApiPort. This will be used to
  709. * queue requests to our API thread instead of doing them inline.
  710. */
  711. Status = ConnectSmWinStationApiPort();
  712. ASSERT( NT_SUCCESS( Status ) );
  713. /*
  714. * Create Console WinStation first
  715. */
  716. Status = WinStationCreateWorker( L"Console", NULL );
  717. if ( !NT_SUCCESS( Status ) ) {
  718. DBGPRINT(("INIT: Failed to create Console WinStation, status=0x%08x\n", Status));
  719. } else {
  720. /*
  721. * From now on,WinStationIdleControlThread can create console sessions if needed
  722. */
  723. InterlockedDecrement(&gConsoleCreationDisable);
  724. }
  725. /*
  726. * Open the Setup key and look for the valuename "SystemSetupInProgress".
  727. * If found and it has a value of TRUE (non-zero), then setup is in
  728. * progress and we skip starting WinStations other than the Console.
  729. */
  730. RtlInitUnicodeString( &KeyPath, SETUP_REG_PATH );
  731. InitializeObjectAttributes( &ObjectAttributes, &KeyPath,
  732. OBJ_CASE_INSENSITIVE, NULL, NULL );
  733. Status = NtOpenKey( &KeyHandle, GENERIC_READ, &ObjectAttributes );
  734. if ( NT_SUCCESS( Status ) ) {
  735. RtlInitUnicodeString( &ValueName, L"SystemSetupInProgress" );
  736. KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
  737. Status = NtQueryValueKey( KeyHandle,
  738. &ValueName,
  739. KeyValuePartialInformation,
  740. (PVOID)KeyValueInfo,
  741. VALUE_BUFFER_SIZE,
  742. &ValueLength );
  743. NtClose( KeyHandle );
  744. if ( NT_SUCCESS( Status ) ) {
  745. ASSERT( ValueLength < VALUE_BUFFER_SIZE );
  746. if ( KeyValueInfo->Type == REG_DWORD &&
  747. KeyValueInfo->DataLength == sizeof(ULONG) &&
  748. *(PULONG)(KeyValueInfo->Data) != 0 ) {
  749. return;
  750. }
  751. }
  752. }
  753. /*
  754. * Start a policy notfiy thread.
  755. *
  756. */
  757. {
  758. HANDLE hGroupPolicyNotifyThread;
  759. DWORD dwID;
  760. g_hMachineGPEvent = CreateEvent (NULL, FALSE, FALSE,
  761. TEXT("TermSrv: machine GP event"));
  762. if (g_hMachineGPEvent)
  763. {
  764. hGroupPolicyNotifyThread = CreateThread (
  765. NULL, 0, (LPTHREAD_START_ROUTINE) GroupPolicyNotifyThread,
  766. 0, 0, &dwID);
  767. if ( hGroupPolicyNotifyThread )
  768. {
  769. NtClose( hGroupPolicyNotifyThread );
  770. }
  771. }
  772. }
  773. /*
  774. * If necessary, create the thread in charge of the regulation of the idle sessions
  775. */
  776. {
  777. HANDLE hIdleControlThread = CreateThread( NULL,
  778. 0, // use Default stack size of the svchost process
  779. (LPTHREAD_START_ROUTINE)WinStationIdleControlThread,
  780. NULL,
  781. THREAD_SET_INFORMATION,
  782. &ThreadId );
  783. if (hIdleControlThread) {
  784. NtClose(hIdleControlThread);
  785. }
  786. }
  787. /*
  788. * Finally, create the terminate thread
  789. */
  790. {
  791. HANDLE hTerminateThread = CreateThread( NULL,
  792. 0, // use Default stack size of the svchost process
  793. (LPTHREAD_START_ROUTINE)WinStationTerminateThread,
  794. NULL,
  795. THREAD_SET_INFORMATION,
  796. &ThreadId );
  797. if ( hTerminateThread )
  798. NtClose( hTerminateThread );
  799. }
  800. }
  801. /*******************************************************************************
  802. *
  803. * StartStopListeners
  804. *
  805. * Get list of configured WinStations from the registry,
  806. * and start the WinStations.
  807. *
  808. * ENTRY:
  809. * bStart
  810. * if TRUE start the listeners.
  811. * if FALSE stop the listeners if no connections related to them exist
  812. * anymore. However we do this only on PRO and PER as on a server we
  813. * don't mind keeping the listeners.
  814. *
  815. * EXIT:
  816. * nothing
  817. *
  818. ******************************************************************************/
  819. BOOL StartStopListeners(LPWSTR WinStationName, BOOLEAN bStart)
  820. {
  821. ULONG i;
  822. ULONG WinStationCount, ByteCount;
  823. PWINSTATIONNAME pBuffer;
  824. PWINSTATIONNAME pWinStationName;
  825. PLIST_ENTRY Head, Next;
  826. PWINSTATION pWinStation;
  827. NTSTATUS Status;
  828. BOOL bReturn = FALSE;
  829. if (bStart) {
  830. /*
  831. * Get list of WinStations from registry
  832. */
  833. pBuffer = NULL;
  834. WinStationCount = 0;
  835. Status = IcaRegWinStationEnumerate( &WinStationCount, NULL, &ByteCount );
  836. if ( NT_SUCCESS(Status) ) {
  837. pBuffer = pWinStationName = MemAlloc( ByteCount );
  838. WinStationCount = (ULONG) -1;
  839. if (pBuffer) {
  840. IcaRegWinStationEnumerate( &WinStationCount,
  841. pWinStationName,
  842. &ByteCount );
  843. }
  844. }
  845. /*
  846. * Now create all remaining WinStations defined in registry
  847. * Note that every 4th WinStation we do the WinStationCreate inline
  848. * instead of queueing it. This is so we don't create an excess
  849. * number of API threads right off the bat.
  850. */
  851. if ( pBuffer ) {
  852. for ( i = 0; i < WinStationCount; i++ ) {
  853. if ( _wcsicmp( pWinStationName, L"Console" ) ) {
  854. if ( i % 4 )
  855. QueueWinStationCreate( pWinStationName );
  856. else // Don't queue more than 4 at a time
  857. (void) WinStationCreateWorker( pWinStationName, NULL );
  858. }
  859. (char *)pWinStationName += sizeof(WINSTATIONNAME);
  860. }
  861. MemFree( pBuffer );
  862. }
  863. bReturn = TRUE;
  864. } else {
  865. //
  866. // Do this only on PRO and PER (i.e. WORKSTATION)
  867. //
  868. if ( gbServer ) {
  869. return FALSE;
  870. }
  871. ENTERCRIT( &WinStationListenersLock );
  872. // Test if TS connections are denied or not in case we are called from a
  873. // terminate or a disconnect.
  874. if ( g_fDenyTSConnectionsPolicy &&
  875. // Performance, we only want to check if policy enable help when connection is denied
  876. (!TSIsMachineInHelpMode() || !TSIsMachinePolicyAllowHelp()) ) {
  877. ULONG ulLogonId;
  878. if( WinStationName ) {
  879. // note that this function doesn't handle renamed listeners
  880. WinStationCount = CountWinStationType( WinStationName, TRUE, FALSE );
  881. if ( WinStationCount == 0 ) {
  882. pWinStation = FindWinStationByName( WinStationName, FALSE );
  883. if ( pWinStation ) {
  884. ulLogonId = pWinStation->LogonId;
  885. ReleaseWinStation( pWinStation );
  886. // reset it and don't recreate it
  887. WinStationResetWorker( ulLogonId, TRUE, FALSE, FALSE );
  888. }
  889. }
  890. } else {
  891. // terminate all listeners
  892. searchagain:
  893. Head = &WinStationListHead;
  894. ENTERCRIT( &WinStationListLock );
  895. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  896. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  897. //
  898. // Only check listening winstations
  899. //
  900. if ( (pWinStation->Flags & WSF_LISTEN) &&
  901. !(pWinStation->Flags & (WSF_RESET | WSF_DELETE)) ) {
  902. ulLogonId = pWinStation->LogonId;
  903. // note that this function doesn't handle renamed listeners
  904. WinStationCount = CountWinStationType( pWinStation->WinStationName, TRUE, TRUE );
  905. if ( WinStationCount == 0 ) {
  906. LEAVECRIT( &WinStationListLock );
  907. // reset it and don't recreate it
  908. WinStationResetWorker( ulLogonId, TRUE, FALSE, FALSE );
  909. goto searchagain;
  910. }
  911. }
  912. }
  913. LEAVECRIT( &WinStationListLock );
  914. }
  915. bReturn = TRUE;
  916. }
  917. LEAVECRIT( &WinStationListenersLock );
  918. }
  919. return bReturn;
  920. }
  921. /*******************************************************************************
  922. * WinStationIdleControlThread
  923. *
  924. * This routine will control the number of idle sessions.
  925. ******************************************************************************/
  926. NTSTATUS WinStationIdleControlThread(PVOID Parameter)
  927. {
  928. ULONG i;
  929. NTSTATUS Status = STATUS_SUCCESS;
  930. PLIST_ENTRY Head, Next;
  931. PWINSTATION pWinStation;
  932. ULONG IdleCount = 0;
  933. ULONG j;
  934. LARGE_INTEGER Timeout;
  935. PLARGE_INTEGER pTimeout;
  936. ULONG ulSleepDuration;
  937. ULONG ulRetries = 0;
  938. ulSleepDuration = 60*1000; // 1 minute
  939. pTimeout = NULL;
  940. /*
  941. * Now create the pool of idle WinStations waiting for a connection.
  942. * we need to wait for termsrv to be fully up, otherwise Winlogon will
  943. * fail its RPC call to termsrv (WaitForConnectWorker).
  944. */
  945. if (gReadyEventHandle != NULL) {
  946. WaitForSingleObject(gReadyEventHandle, (DWORD)-1);
  947. }
  948. for ( i = 0; i < IdleWinStationPoolCount; i++ ) {
  949. (void) WinStationCreateWorker( NULL, NULL );
  950. }
  951. Timeout = RtlEnlargedIntegerMultiply( ulSleepDuration, -10000);
  952. if (WinStationIdleControlEvent != NULL)
  953. {
  954. while (TRUE)
  955. {
  956. Status = NtWaitForSingleObject(WinStationIdleControlEvent,FALSE, pTimeout);
  957. if ( !NT_SUCCESS(Status) && (Status != STATUS_TIMEOUT)) {
  958. Sleep(1000); // don't eat too much CPU
  959. continue;
  960. }
  961. pTimeout = &Timeout;
  962. /*
  963. * See if we need to create a console session
  964. * If if we fail creating a console session, we'll set a timeout so that we will
  965. * retry .
  966. */
  967. ENTERCRIT( &ConsoleLock );
  968. if (gConsoleCreationDisable == 0) {
  969. if (WinStationCheckConsoleSession()) {
  970. pTimeout = NULL;
  971. }
  972. }
  973. LEAVECRIT( &ConsoleLock );
  974. /*
  975. * Now count the number of IDLE WinStations and ensure there
  976. * are enough available.
  977. */
  978. if (IdleWinStationPoolCount != 0) {
  979. ENTERCRIT( &WinStationListLock );
  980. IdleCount = 0;
  981. Head = &WinStationListHead;
  982. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  983. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  984. if ( pWinStation->Flags & WSF_IDLE )
  985. IdleCount++;
  986. }
  987. LEAVECRIT( &WinStationListLock );
  988. for ( j = IdleCount; j < IdleWinStationPoolCount; j++ ) {
  989. WinStationCreateWorker( NULL, NULL );
  990. }
  991. }
  992. }
  993. }
  994. return STATUS_SUCCESS;
  995. }
  996. #if _MSC_FULL_VER >= 13008827
  997. #pragma warning(push)
  998. #pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
  999. #endif
  1000. /*******************************************************************************
  1001. * WinStationTerminateThread
  1002. *
  1003. * This routine will wait for WinStation processes to terminate,
  1004. * and will then reset the corresponding WinStation.
  1005. ******************************************************************************/
  1006. NTSTATUS WinStationTerminateThread(PVOID Parameter)
  1007. {
  1008. LONG ThreadIndex = (LONG)(INT_PTR)Parameter;
  1009. LONG WinStationIndex;
  1010. PLIST_ENTRY Head, Next;
  1011. PWINSTATION pWinStation;
  1012. LONG EventCount;
  1013. LONG EventIndex;
  1014. int WaitCount;
  1015. int HandleCount;
  1016. int HandleArraySize = 0;
  1017. PHANDLE pHandleArray = NULL;
  1018. PULONG pIdArray = NULL;
  1019. ULONG ThreadsNeeded;
  1020. ULONG ThreadsRunning = 1;
  1021. ULONG j;
  1022. NTSTATUS Status;
  1023. LARGE_INTEGER Timeout;
  1024. PLARGE_INTEGER pTimeout;
  1025. ULONG ulSleepDuration;
  1026. /*
  1027. * We need some timer values for the diferent cases of failure in
  1028. * the thread's loop:
  1029. * - If we fail creating a new WinstationterminateThread,
  1030. * then we will do a WaitFormulpipleObjects with a timer instead of a wait without
  1031. * time out. This will give a new chance to create the thread when timout is over.
  1032. * if we fail allocating a new buffer to extend handle array, we will wait a timeout
  1033. * duration before we retry.
  1034. */
  1035. ulSleepDuration = 3*60*1000;
  1036. Timeout = RtlEnlargedIntegerMultiply( ulSleepDuration, -10000);
  1037. /*
  1038. * Loop forever waiting for WinStation processes to terminate
  1039. */
  1040. for ( ; ; ) {
  1041. /*
  1042. * Determine number of WinStations
  1043. */
  1044. pTimeout = NULL;
  1045. WaitCount = 0;
  1046. Head = &WinStationListHead;
  1047. ENTERCRIT( &WinStationListLock );
  1048. for ( Next = Head->Flink; Next != Head; Next = Next->Flink )
  1049. WaitCount++;
  1050. /*
  1051. * If there are more than the maximum number of objects that
  1052. * can be specified to NtWaitForMultipleObjects, then determine
  1053. * if we must start up additional thread(s).
  1054. */
  1055. if ( WaitCount > MAXIMUM_WAIT_WINSTATIONS ) {
  1056. ThreadsNeeded = (WaitCount + MAXIMUM_WAIT_WINSTATIONS - 1) /
  1057. MAXIMUM_WAIT_WINSTATIONS;
  1058. WaitCount = MAXIMUM_WAIT_WINSTATIONS;
  1059. if ( ThreadIndex == 0 && ThreadsNeeded > ThreadsRunning ) {
  1060. LEAVECRIT( &WinStationListLock );
  1061. for ( j = ThreadsRunning; j < ThreadsNeeded; j++ ) {
  1062. DWORD ThreadId;
  1063. HANDLE Handle;
  1064. Handle = CreateThread( NULL,
  1065. 0, // use Default stack size of the svchost process
  1066. (LPTHREAD_START_ROUTINE)
  1067. WinStationTerminateThread,
  1068. ULongToPtr( j * MAXIMUM_WAIT_WINSTATIONS ),
  1069. THREAD_SET_INFORMATION,
  1070. &ThreadId );
  1071. if ( !Handle ) {
  1072. pTimeout = &Timeout;
  1073. break;
  1074. }
  1075. // makarp: 182597 - close handle to the thread.
  1076. CloseHandle(Handle);
  1077. ThreadsRunning++;
  1078. }
  1079. ENTERCRIT( &WinStationListLock );
  1080. }
  1081. }
  1082. /*
  1083. * If we need a larger handle array, then release the
  1084. * WinStationList lock, allocate the new handle array,
  1085. * and go start the loop again.
  1086. */
  1087. HandleCount = (WaitCount << 1) + 1;
  1088. ASSERT( HandleCount < MAXIMUM_WAIT_OBJECTS );
  1089. if ( HandleCount > HandleArraySize ||
  1090. HandleCount < HandleArraySize - 10 ) {
  1091. LEAVECRIT( &WinStationListLock );
  1092. if ( pHandleArray ){
  1093. MemFree( pHandleArray );
  1094. }
  1095. pHandleArray = MemAlloc( HandleCount * sizeof(HANDLE) );
  1096. if ( pIdArray ) {
  1097. MemFree( pIdArray );
  1098. }
  1099. pIdArray = MemAlloc( HandleCount * sizeof(ULONG) );
  1100. /* makarp: check for allocation failures #182597 */
  1101. if (!pIdArray || !pHandleArray) {
  1102. if (pIdArray) {
  1103. MemFree(pIdArray);
  1104. pIdArray = NULL;
  1105. }
  1106. if (pHandleArray){
  1107. MemFree(pHandleArray);
  1108. pHandleArray = NULL;
  1109. }
  1110. HandleArraySize = 0;
  1111. Sleep(ulSleepDuration);
  1112. continue;
  1113. }
  1114. HandleArraySize = HandleCount;
  1115. continue;
  1116. }
  1117. /*
  1118. * Build list of handles to wait on
  1119. */
  1120. EventCount = 0;
  1121. pIdArray[EventCount] = 0;
  1122. pHandleArray[EventCount++] = WinStationEvent;
  1123. WinStationIndex = 0;
  1124. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  1125. if ( WinStationIndex++ < ThreadIndex )
  1126. continue;
  1127. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  1128. if ( !pWinStation->LogonId ) // no waiting on console
  1129. continue;
  1130. if ( pWinStation->Starting )
  1131. continue;
  1132. if (pWinStation->Terminating) {
  1133. continue;
  1134. }
  1135. if ( pWinStation->WindowsSubSysProcess ) {
  1136. pIdArray[EventCount] = pWinStation->LogonId;
  1137. pHandleArray[EventCount++] = pWinStation->WindowsSubSysProcess;
  1138. }
  1139. if ( pWinStation->InitialCommandProcess ) {
  1140. pIdArray[EventCount] = pWinStation->LogonId;
  1141. pHandleArray[EventCount++] = pWinStation->InitialCommandProcess;
  1142. }
  1143. if ( WinStationIndex - ThreadIndex >= WaitCount )
  1144. break;
  1145. }
  1146. /*
  1147. * Reset WinStationEvent and release the WinStationList lock
  1148. */
  1149. NtResetEvent( WinStationEvent, NULL );
  1150. LEAVECRIT( &WinStationListLock );
  1151. /*
  1152. * Wait for WinStationEvent to trigger (meaning that the
  1153. * WinStationList has changed), or for one of the existing
  1154. * Win32 subsystems or initial commands to terminate.
  1155. */
  1156. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: TerminateThread, Waiting for initial command exit (ArraySize=%d)\n", EventCount ));
  1157. Status = NtWaitForMultipleObjects( EventCount, pHandleArray, WaitAny,
  1158. FALSE, pTimeout );
  1159. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: TerminateThread, WaitForMultipleObjects, rc=%x\n", Status ));
  1160. if ( !NT_SUCCESS(Status) || Status >= EventCount ) { //WinStationVerifyHandles();
  1161. continue;
  1162. }
  1163. /*
  1164. * If WinStationEvent triggered, then just go recompute handle list
  1165. */
  1166. if ( (EventIndex = Status) == STATUS_WAIT_0 )
  1167. continue;
  1168. /*
  1169. * Find the WinStation for the process that terminated and
  1170. * mark it as terminating. This prevents us from waiting
  1171. * on that WinStation's processes next time through the loop.
  1172. * (NOTE: The 'Terminating' field is protected by the global
  1173. * WinStationListLock instead of the WinStation mutex.)
  1174. */
  1175. Head = &WinStationListHead;
  1176. ENTERCRIT( &WinStationListLock );
  1177. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  1178. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  1179. if ( pWinStation->LogonId == pIdArray[EventIndex] ) {
  1180. pWinStation->Terminating = TRUE;
  1181. break;
  1182. }
  1183. }
  1184. LEAVECRIT( &WinStationListLock );
  1185. /*
  1186. * Wake up the WinStationIdleControlThread
  1187. */
  1188. NtSetEvent(WinStationIdleControlEvent, NULL);
  1189. /*
  1190. * If there are multiple terminate threads, cause the other
  1191. * threads to wakeup in order to recompute their wait lists.
  1192. */
  1193. NtSetEvent( WinStationEvent, NULL );
  1194. /*
  1195. * One of the initial command processes has terminated,
  1196. * queue a request to reset the WinStation.
  1197. */
  1198. QueueWinStationReset( pIdArray[EventIndex]);
  1199. }
  1200. // make the compiler happy
  1201. return STATUS_SUCCESS;
  1202. }
  1203. #if _MSC_FULL_VER >= 13008827
  1204. #pragma warning(pop)
  1205. #endif
  1206. /*******************************************************************************
  1207. * InvalidateTerminateWaitList
  1208. *
  1209. * Wakes up WinStationTerminateThread to force it to reinitialize its
  1210. * wait list. Used when we detect that the initial process was ntsd,
  1211. * and we change the initial process to WinLogon.
  1212. *
  1213. * ENTRY:
  1214. * The WinStationListLock must not be held.
  1215. ******************************************************************************/
  1216. VOID InvalidateTerminateWaitList(void)
  1217. {
  1218. ENTERCRIT( &WinStationListLock );
  1219. NtSetEvent( WinStationEvent, NULL );
  1220. LEAVECRIT( &WinStationListLock );
  1221. }
  1222. /*******************************************************************************
  1223. * WinStationConnectThread
  1224. *
  1225. * This routine will wait for and process incoming connections
  1226. * for a specified WinStation.
  1227. ******************************************************************************/
  1228. NTSTATUS WinStationConnectThread(ULONG Parameter)
  1229. {
  1230. typedef struct _TRANSFERTHREAD {
  1231. LIST_ENTRY Entry;
  1232. HANDLE hThread;
  1233. } TRANSFERTHREAD, *PTRANSFERTHREAD;
  1234. LIST_ENTRY TransferThreadList;
  1235. PTRANSFERTHREAD pTransferThread;
  1236. PLIST_ENTRY Next;
  1237. PWINSTATION pListenWinStation;
  1238. PVOID pEndpoint = NULL;
  1239. ULONG EndpointLength;
  1240. ULONG WinStationCount;
  1241. ULONG TransferThreadCount;
  1242. BOOLEAN rc;
  1243. BOOLEAN bConnectSuccess = FALSE;
  1244. BOOLEAN bTerminate = FALSE;
  1245. NTSTATUS Status;
  1246. SYSTEMTIME currentSystemTime;
  1247. #define MODULE_SIZE 1024
  1248. #define _WAIT_ERROR_LIMIT 10
  1249. ULONG WaitErrorLimit = _WAIT_ERROR_LIMIT; // # of consecutive errors allowed
  1250. /*
  1251. * Initialize list of transfer threads
  1252. */
  1253. InitializeListHead( &TransferThreadList );
  1254. /*
  1255. * Find and lock the WinStation
  1256. */
  1257. pListenWinStation = FindWinStationById( Parameter, FALSE );
  1258. if ( pListenWinStation == NULL ) {
  1259. return( STATUS_ACCESS_DENIED );
  1260. }
  1261. /*
  1262. * Ensure only authorized Session Driver and Video Driver stack
  1263. * modules will be loaded as a result of this connection thread.
  1264. *
  1265. * If any module fails verification, mark the WinStation in the
  1266. * DOWN state and exit WITHOUT error.
  1267. *
  1268. * NOTE:
  1269. * The silent exit is very much intentional so as not to aid in
  1270. * a third party's attempt to circumvent this security measure.
  1271. */
  1272. Status = _VerifyStackModules( pListenWinStation );
  1273. if ( Status != STATUS_SUCCESS ) {
  1274. pListenWinStation->State = State_Down;
  1275. ReleaseWinStation( pListenWinStation );
  1276. return( STATUS_SUCCESS );
  1277. }
  1278. /*
  1279. * Indicate we got this far successfully.
  1280. */
  1281. pListenWinStation->CreateStatus = STATUS_SUCCESS;
  1282. /*
  1283. * Load the WinStation extension dll for this WinStation.
  1284. * Note that we don't save the result in pListenWinStation->pWsx
  1285. * since we don't want to make callouts to it for the
  1286. * listen WinStation.
  1287. */
  1288. (VOID) FindWinStationExtensionDll( pListenWinStation->Config.Wd.WsxDLL,
  1289. pListenWinStation->Config.Wd.WdFlag );
  1290. /*
  1291. * Do not start accepting client connections before termsrv is totaly UP
  1292. */
  1293. if (gReadyEventHandle != NULL) {
  1294. WaitForSingleObject(gReadyEventHandle, (DWORD)-1);
  1295. }
  1296. /*
  1297. * for perf reason termsrv startup is delayed. We the need to also delay
  1298. * accepting connections so that if a console logon hapened before termsrv
  1299. * was up, we get the delayed logon notification before we accept a
  1300. * client connection.
  1301. */
  1302. if (gbFirtsConnectionThread) {
  1303. Sleep(5*1000);
  1304. gbFirtsConnectionThread = FALSE;
  1305. }
  1306. /*
  1307. * Loop waiting for connection requests and passing them off
  1308. * to an idle WinStation.
  1309. */
  1310. for ( ; ; ) {
  1311. /*
  1312. * Abort retries if this listener has been terminated
  1313. */
  1314. if ( pListenWinStation->Terminating ) {
  1315. break;
  1316. }
  1317. /*
  1318. * Allocate an endpoint buffer
  1319. */
  1320. pEndpoint = MemAlloc( MODULE_SIZE );
  1321. if ( !pEndpoint ) {
  1322. Status = STATUS_NO_MEMORY;
  1323. // Sleep for 30 seconds and try again. Listener thread should not exit
  1324. // simply just in low memory condition
  1325. UnlockWinStation(pListenWinStation);
  1326. Sleep(30000);
  1327. if (!RelockWinStation(pListenWinStation))
  1328. break;
  1329. continue;
  1330. }
  1331. /*
  1332. * Unlock listen WinStation while we wait for a connection
  1333. */
  1334. UnlockWinStation( pListenWinStation );
  1335. /*
  1336. * Check if # outstanding connections reaches max value
  1337. * If so, wait for the event when the connection # drops
  1338. * below the max. There is a timeout value of 30 seconds
  1339. * for the wait
  1340. */
  1341. if (hConnectEvent != NULL) {
  1342. if (NumOutStandingConnect > MaxOutStandingConnect) {
  1343. DWORD rc;
  1344. // Event log we have exceeded max outstanding connections. but not more than
  1345. // once in a day.
  1346. GetSystemTime(&currentSystemTime);
  1347. if ( currentSystemTime.wYear != LastLoggedDelayConnection.wYear ||
  1348. currentSystemTime.wMonth != LastLoggedDelayConnection.wMonth ||
  1349. currentSystemTime.wDay != LastLoggedDelayConnection.wDay ||
  1350. gbNeverLoggedDelayConnection
  1351. ) {
  1352. gbNeverLoggedDelayConnection = FALSE;
  1353. LastLoggedDelayConnection = currentSystemTime;
  1354. WriteErrorLogEntry(EVENT_TOO_MANY_CONNECTIONS,
  1355. pListenWinStation->WinStationName,
  1356. sizeof(pListenWinStation->WinStationName));
  1357. }
  1358. // manual reset the ConnectEvent before wait
  1359. ResetEvent(hConnectEvent);
  1360. rc = WAIT_TIMEOUT;
  1361. // wait for Connect Event for 30 secs
  1362. while (rc == WAIT_TIMEOUT) {
  1363. rc = WaitForSingleObject(hConnectEvent, DelayConnectionTime);
  1364. if (NumOutStandingConnect <= MaxOutStandingConnect) {
  1365. break;
  1366. }
  1367. if (rc == WAIT_TIMEOUT) {
  1368. KdPrint(("TermSrv: Reached 30 secs timeout\n"));
  1369. }
  1370. else {
  1371. KdPrint(("TermSrv: WaitForSingleObject return status=%x\n", rc));
  1372. }
  1373. }
  1374. }
  1375. }
  1376. /*
  1377. * Wait for connection
  1378. */
  1379. Status = IcaStackConnectionWait( pListenWinStation->hStack,
  1380. pListenWinStation->WinStationName,
  1381. &pListenWinStation->Config,
  1382. NULL,
  1383. pEndpoint,
  1384. MODULE_SIZE,
  1385. &EndpointLength );
  1386. if ( Status == STATUS_BUFFER_TOO_SMALL ) {
  1387. MemFree( pEndpoint );
  1388. pEndpoint = MemAlloc( EndpointLength );
  1389. if ( !pEndpoint ) {
  1390. Status = STATUS_NO_MEMORY;
  1391. // Sleep for 30 seconds and try again. Listener thread should not exit
  1392. // simply just in low memory condition
  1393. Sleep(30000);
  1394. if (!RelockWinStation( pListenWinStation ))
  1395. break;
  1396. continue;
  1397. }
  1398. Status = IcaStackConnectionWait( pListenWinStation->hStack,
  1399. pListenWinStation->WinStationName,
  1400. &pListenWinStation->Config,
  1401. NULL,
  1402. pEndpoint,
  1403. EndpointLength,
  1404. &EndpointLength );
  1405. }
  1406. /*
  1407. * If ConnectionWait was not successful,
  1408. * check to see if the consecutive error limit has been reached.
  1409. */
  1410. if ( !NT_SUCCESS( Status ) ) {
  1411. MemFree( pEndpoint );
  1412. pEndpoint = NULL;
  1413. /*
  1414. * If status is DEVICE_DOES_NOT_EXIST, then we want to wait before retrying
  1415. * otherwise, this prioritary thread will take all the CPU trying 10 times
  1416. * lo load the listener stack. Such an error takes time to be fixed (either
  1417. * changing the NIC or going into tscc to have the NIC GUID table updated.
  1418. */
  1419. if ((Status == STATUS_DEVICE_DOES_NOT_EXIST) || (!bConnectSuccess) || (Status == STATUS_INVALID_ADDRESS_COMPONENT) ) {
  1420. Sleep(30000);
  1421. }
  1422. if ( WaitErrorLimit--) {
  1423. if (!RelockWinStation( pListenWinStation ))
  1424. break;
  1425. /*
  1426. * If we have had a successful connection,
  1427. * then skip the stack close/reopen since this would
  1428. * terminate any existing connections.
  1429. */
  1430. if ( !bConnectSuccess ) {
  1431. /*
  1432. * What we really need is a function to unload the
  1433. * stack drivers but leave the stack handle open.
  1434. */
  1435. Status = IcaStackClose( pListenWinStation->hStack );
  1436. ASSERT( NT_SUCCESS( Status ) );
  1437. Status = IcaStackOpen( pListenWinStation->hIca,
  1438. Stack_Primary,
  1439. (PROC)WsxStackIoControl,
  1440. pListenWinStation,
  1441. &pListenWinStation->hStack );
  1442. if ( !NT_SUCCESS( Status ) ) {
  1443. pListenWinStation->State = State_Down;
  1444. break;
  1445. }
  1446. }
  1447. continue;
  1448. }
  1449. else {
  1450. // Sleep for 30 seconds and try again. Listener thread should not exit
  1451. Sleep(30000);
  1452. if (!RelockWinStation( pListenWinStation ))
  1453. break;
  1454. // Reset the error count
  1455. WaitErrorLimit = _WAIT_ERROR_LIMIT;
  1456. continue;
  1457. }
  1458. } else {
  1459. bConnectSuccess = TRUE;
  1460. WaitErrorLimit = _WAIT_ERROR_LIMIT;
  1461. }
  1462. /*
  1463. * Check for Shutdown and MaxInstance
  1464. */
  1465. rc = RelockWinStation( pListenWinStation );
  1466. if ( !rc )
  1467. break;
  1468. /*
  1469. * Reject all connections if a shutdown is in progress
  1470. */
  1471. if ( ShutdownInProgress ) {
  1472. Status = _CloseEndpoint( &pListenWinStation->Config,
  1473. pEndpoint,
  1474. EndpointLength,
  1475. pListenWinStation,
  1476. TRUE ); // Use a temporary stack
  1477. MemFree( pEndpoint );
  1478. pEndpoint = NULL;
  1479. continue;
  1480. }
  1481. /*
  1482. * Reject all connections if user or the Group-Policy has disabled accepting connections
  1483. */
  1484. if ( g_fDenyTSConnectionsPolicy )
  1485. {
  1486. //
  1487. // Performance, we only want to check if policy enable help when connection is denied
  1488. //
  1489. if( !TSIsMachineInHelpMode() || !TSIsMachinePolicyAllowHelp() )
  1490. {
  1491. Status = _CloseEndpoint( &pListenWinStation->Config,
  1492. pEndpoint,
  1493. EndpointLength,
  1494. pListenWinStation,
  1495. TRUE ); // Use a temporary stack
  1496. MemFree( pEndpoint );
  1497. pEndpoint = NULL;
  1498. if ( !gbServer ) {
  1499. //
  1500. // On Personal and Pro if there are no more connections associated
  1501. // to this listener, then terminate it.
  1502. //
  1503. // note that this function doesn't handle renamed listeners
  1504. WinStationCount = CountWinStationType( pListenWinStation->WinStationName, TRUE, FALSE );
  1505. if ( WinStationCount == 0 ) {
  1506. bTerminate = TRUE;
  1507. break;
  1508. }
  1509. }
  1510. Sleep( 5000 ); // sleep for 5 seconds, defense against
  1511. // denial of service attacks.
  1512. continue;
  1513. }
  1514. }
  1515. /*
  1516. * Check to see how many transfer threads we have active.
  1517. * If more than the MaxInstance count, we won't start any more.
  1518. */
  1519. TransferThreadCount = 0;
  1520. Next = TransferThreadList.Flink;
  1521. while ( Next != &TransferThreadList ) {
  1522. pTransferThread = CONTAINING_RECORD( Next, TRANSFERTHREAD, Entry );
  1523. Next = Next->Flink;
  1524. /*
  1525. * If thread is still active, bump the thread count
  1526. */
  1527. if ( WaitForSingleObject( pTransferThread->hThread, 0 ) != 0 ) {
  1528. TransferThreadCount++;
  1529. /*
  1530. * Thread has exited, so close the thread handle and free memory
  1531. */
  1532. } else {
  1533. RemoveEntryList( &pTransferThread->Entry );
  1534. CloseHandle( pTransferThread->hThread );
  1535. MemFree( pTransferThread );
  1536. }
  1537. }
  1538. /*
  1539. * If this is not a single-instance connection
  1540. * and there is a MaxInstance count specified,
  1541. * then check whether the MaxInstance limit will be exceeded.
  1542. */
  1543. if ( !(pListenWinStation->Config.Pd[0].Create.PdFlag & PD_SINGLE_INST) &&
  1544. pListenWinStation->Config.Create.MaxInstanceCount != (ULONG)-1 ) {
  1545. ULONG Count;
  1546. /*
  1547. * Count number of currently active WinStations
  1548. */
  1549. WinStationCount = CountWinStationType( pListenWinStation->WinStationName, FALSE, FALSE );
  1550. /*
  1551. * Get larger of WinStation and TransferThread count
  1552. */
  1553. Count = max( WinStationCount, TransferThreadCount );
  1554. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: Count %d\n", Count ));
  1555. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: MaxInstanceCount %d\n", pListenWinStation->Config.Create.MaxInstanceCount ));
  1556. if ( pListenWinStation->Config.Create.MaxInstanceCount <= Count ) {
  1557. Status = _CloseEndpoint( &pListenWinStation->Config,
  1558. pEndpoint,
  1559. EndpointLength,
  1560. pListenWinStation,
  1561. TRUE ); // Use a temporary stack
  1562. MemFree( pEndpoint );
  1563. pEndpoint = NULL;
  1564. continue;
  1565. }
  1566. }
  1567. UnlockWinStation( pListenWinStation );
  1568. /*
  1569. * Increment the counter of pending connections.
  1570. */
  1571. InterlockedIncrement( &NumOutStandingConnect );
  1572. /*
  1573. * If this is a single instance connection,
  1574. * then handle the transfer of the connection endpoint to
  1575. * an idle WinStation directly.
  1576. */
  1577. if ( pListenWinStation->Config.Pd[0].Create.PdFlag & PD_SINGLE_INST ) {
  1578. Status = TransferConnectionToIdleWinStation( pListenWinStation,
  1579. pEndpoint,
  1580. EndpointLength,
  1581. NULL );
  1582. pEndpoint = NULL;
  1583. /*
  1584. * If the transfer was successful, then for a single instance
  1585. * connection, we must now exit the wait for connect loop.
  1586. */
  1587. if ( NT_SUCCESS( Status ) ) {
  1588. RelockWinStation( pListenWinStation );
  1589. break;
  1590. }
  1591. /*
  1592. * For non-single instance WinStations, let the worker thread
  1593. * handle the connection handoff so this thread can go back
  1594. * and listen for another connection immediately.
  1595. */
  1596. } else {
  1597. PTRANSFER_INFO pInfo;
  1598. DWORD ThreadId;
  1599. HANDLE hTransferThread;
  1600. BOOLEAN bTransferThreadCreated = FALSE;
  1601. pInfo = MemAlloc( sizeof(*pInfo) );
  1602. pTransferThread = MemAlloc( sizeof(*pTransferThread) );
  1603. if (pInfo && pTransferThread) {
  1604. pInfo->LogonId = pListenWinStation->LogonId;
  1605. pInfo->pEndpoint = pEndpoint;
  1606. pInfo->EndpointLength = EndpointLength;
  1607. pTransferThread->hThread = CreateThread(
  1608. NULL,
  1609. 0, // use Default stack size of the svchost process
  1610. (LPTHREAD_START_ROUTINE)WinStationTransferThread,
  1611. (PVOID)pInfo,
  1612. 0,
  1613. &ThreadId
  1614. );
  1615. if ( pTransferThread->hThread ) {
  1616. bTransferThreadCreated = TRUE;
  1617. InsertTailList( &TransferThreadList, &pTransferThread->Entry );
  1618. }
  1619. }
  1620. if (!bTransferThreadCreated) {
  1621. if (pInfo) {
  1622. MemFree( pInfo );
  1623. }
  1624. if (pTransferThread) {
  1625. MemFree( pTransferThread );
  1626. }
  1627. TransferConnectionToIdleWinStation( pListenWinStation,
  1628. pEndpoint,
  1629. EndpointLength,
  1630. NULL );
  1631. }
  1632. pEndpoint = NULL;
  1633. }
  1634. /*
  1635. * Relock the listen WinStation
  1636. */
  1637. if (!RelockWinStation( pListenWinStation ) )
  1638. break;
  1639. } // for - wait for connection
  1640. /*
  1641. * Clean up the transfer thread list.
  1642. * (There's no need to wait for them to exit.)
  1643. */
  1644. Next = TransferThreadList.Flink;
  1645. while ( Next != &TransferThreadList ) {
  1646. pTransferThread = CONTAINING_RECORD( Next, TRANSFERTHREAD, Entry );
  1647. Next = Next->Flink;
  1648. RemoveEntryList( &pTransferThread->Entry );
  1649. CloseHandle( pTransferThread->hThread );
  1650. MemFree( pTransferThread );
  1651. }
  1652. /*
  1653. * If after exiting the connect loop above, the WinStation is marked down,
  1654. * then write the error status to the event log.
  1655. */
  1656. if ( pListenWinStation->State == State_Down ) {
  1657. ReleaseWinStation( pListenWinStation );
  1658. if ( Status != STATUS_CTX_CLOSE_PENDING ) {
  1659. PostErrorValueEvent(EVENT_TS_LISTNER_WINSTATION_ISDOWN, Status);
  1660. }
  1661. } else {
  1662. /*
  1663. * If not a single-instance transport release the WinStation;
  1664. * otherwise, delete the listener WinStation.
  1665. */
  1666. if (!(pListenWinStation->Config.Pd[0].Create.PdFlag & PD_SINGLE_INST) &&
  1667. !bTerminate) {
  1668. ReleaseWinStation( pListenWinStation );
  1669. } else {
  1670. /*
  1671. * Mark the listen winstation as being deleted.
  1672. * If a reset/delete operation is already in progress
  1673. * on this winstation, then don't proceed with the delete.
  1674. */
  1675. if ( pListenWinStation->Flags & (WSF_RESET | WSF_DELETE) ) {
  1676. ReleaseWinStation( pListenWinStation );
  1677. Status = STATUS_CTX_WINSTATION_BUSY;
  1678. goto done;
  1679. }
  1680. pListenWinStation->Flags |= WSF_DELETE;
  1681. /*
  1682. * Make sure this WinStation is ready to delete
  1683. */
  1684. WinStationTerminate( pListenWinStation );
  1685. /*
  1686. * Call the WinStationDelete worker
  1687. */
  1688. WinStationDeleteWorker( pListenWinStation );
  1689. }
  1690. }
  1691. done:
  1692. if ( pEndpoint ){
  1693. Status = _CloseEndpoint( &pListenWinStation->Config,
  1694. pEndpoint,
  1695. EndpointLength,
  1696. pListenWinStation,
  1697. TRUE ); // Use a temporary stack
  1698. MemFree( pEndpoint );
  1699. }
  1700. return Status;
  1701. }
  1702. /*******************************************************************************
  1703. * WinStationTransferThread
  1704. ******************************************************************************/
  1705. NTSTATUS WinStationTransferThread(PVOID Parameter)
  1706. {
  1707. PTRANSFER_INFO pInfo;
  1708. PWINSTATION pListenWinStation;
  1709. NTSTATUS Status;
  1710. /*
  1711. * Find and lock the listen WinStation
  1712. * (We MUST do this so that it doesn't get deleted while
  1713. * we are attempting to transfer the new connection.)
  1714. */
  1715. pInfo = (PTRANSFER_INFO)Parameter;
  1716. pListenWinStation = FindWinStationById( pInfo->LogonId, FALSE );
  1717. if ( pListenWinStation == NULL ) {
  1718. MemFree( pInfo );
  1719. if( InterlockedDecrement( &NumOutStandingConnect ) == MaxOutStandingConnect )
  1720. {
  1721. if (hConnectEvent != NULL)
  1722. {
  1723. SetEvent(hConnectEvent);
  1724. }
  1725. }
  1726. return( STATUS_ACCESS_DENIED );
  1727. }
  1728. /*
  1729. * Unlock the listen WinStation but hold a reference to it.
  1730. */
  1731. UnlockWinStation( pListenWinStation );
  1732. /*
  1733. * Transfer the connection to an idle WinStation
  1734. */
  1735. Status = TransferConnectionToIdleWinStation( pListenWinStation,
  1736. pInfo->pEndpoint,
  1737. pInfo->EndpointLength,
  1738. NULL );
  1739. /*
  1740. * Relock and release the listen WinStation
  1741. */
  1742. RelockWinStation( pListenWinStation );
  1743. ReleaseWinStation( pListenWinStation );
  1744. MemFree(pInfo);
  1745. return Status;
  1746. }
  1747. NTSTATUS TransferConnectionToIdleWinStation(
  1748. PWINSTATION pListenWinStation,
  1749. PVOID pEndpoint,
  1750. ULONG EndpointLength,
  1751. PICA_STACK_ADDRESS pStackAddress)
  1752. {
  1753. PWINSTATION pTargetWinStation = NULL;
  1754. ULONG ReturnLength;
  1755. BOOLEAN rc;
  1756. BOOLEAN CreatedIdle = FALSE;
  1757. BOOLEAN ConnectionAccepted = FALSE;
  1758. NTSTATUS Status;
  1759. ICA_TRACE Trace;
  1760. LS_STATUS_CODE LlsStatus;
  1761. NT_LS_DATA LsData;
  1762. BOOLEAN bBlockThis;
  1763. PWCHAR pListenName;
  1764. PWINSTATIONCONFIG2 pConfig;
  1765. BOOL bPolicyAllowHelp;
  1766. BYTE in_addr[16];
  1767. UINT uAddrSize;
  1768. BOOL bSuccessAdded = FALSE;
  1769. WINSTATIONNAME szDefaultConfigWinstationName;
  1770. BOOL bCanCallout;
  1771. // error code we need to pass back to client
  1772. NTSTATUS StatusCallback = STATUS_SUCCESS;
  1773. // Flag to detemine if session is a RA login
  1774. BOOL bSessionIsHelpSession;
  1775. BOOL bValidRAConnect;
  1776. //
  1777. // Check AllowGetHelp policy is enabled and salem has pending help session
  1778. //
  1779. bPolicyAllowHelp = TSIsMachinePolicyAllowHelp() & TSIsMachineInHelpMode();
  1780. if( g_fDenyTSConnectionsPolicy && !bPolicyAllowHelp )
  1781. {
  1782. //
  1783. // Close the connection if TS policy deny connection and
  1784. // help is disabled.
  1785. //
  1786. TRACE((hTrace, TC_ICASRV, TT_ERROR,
  1787. "TERMSRV: Denying TS connection due to GP\n"));
  1788. if ( pListenWinStation && pEndpoint ) {
  1789. Status = _CloseEndpoint( &pListenWinStation->Config,
  1790. pEndpoint,
  1791. EndpointLength,
  1792. pListenWinStation,
  1793. TRUE ); // Use a temporary stack
  1794. MemFree(pEndpoint);
  1795. pEndpoint = NULL;
  1796. }
  1797. if( InterlockedDecrement( &NumOutStandingConnect ) == MaxOutStandingConnect )
  1798. {
  1799. if (hConnectEvent != NULL)
  1800. {
  1801. SetEvent(hConnectEvent);
  1802. }
  1803. }
  1804. return STATUS_CTX_WINSTATION_ACCESS_DENIED;
  1805. }
  1806. //
  1807. // check for rejected connections
  1808. //
  1809. if( pListenWinStation )
  1810. {
  1811. uAddrSize = sizeof( in_addr );
  1812. bSuccessAdded = Filter_AddOutstandingConnection(
  1813. pListenWinStation->hStack,
  1814. pEndpoint,
  1815. EndpointLength,
  1816. in_addr,
  1817. &uAddrSize,
  1818. &bBlockThis
  1819. );
  1820. //
  1821. // connection blocked, close and exit
  1822. //
  1823. if ( bBlockThis )
  1824. {
  1825. TRACE((hTrace, TC_ICASRV, TT_ERROR,
  1826. "TERMSRV: Excessive number of pending connections\n"));
  1827. if ( bSuccessAdded )
  1828. {
  1829. Filter_RemoveOutstandingConnection( in_addr, uAddrSize );
  1830. }
  1831. Status = _CloseEndpoint( &pListenWinStation->Config,
  1832. pEndpoint,
  1833. EndpointLength,
  1834. pListenWinStation,
  1835. TRUE ); // Use a temporary stack
  1836. MemFree( pEndpoint );
  1837. pEndpoint = NULL;
  1838. if( InterlockedDecrement( &NumOutStandingConnect ) == MaxOutStandingConnect )
  1839. {
  1840. if (hConnectEvent != NULL)
  1841. {
  1842. SetEvent(hConnectEvent);
  1843. }
  1844. }
  1845. return STATUS_CTX_WINSTATION_ACCESS_DENIED;
  1846. }
  1847. }
  1848. else
  1849. {
  1850. // Make sure variable
  1851. bBlockThis = FALSE;
  1852. bSuccessAdded = FALSE;
  1853. }
  1854. //
  1855. //
  1856. /*
  1857. * Now find an idle WinStation to transfer this connection to.
  1858. * If there is not one available, then we attempt to create one.
  1859. * if this also fails, then we have no choice but to close
  1860. * the connection endpoint and wait again.
  1861. */
  1862. pTargetWinStation = FindIdleWinStation();
  1863. if ( pTargetWinStation == NULL ) {
  1864. /*
  1865. * Create another idle WinStation
  1866. */
  1867. Status = WinStationCreateWorker( NULL, NULL );
  1868. if ( NT_SUCCESS( Status ) )
  1869. CreatedIdle = TRUE;
  1870. pTargetWinStation = FindIdleWinStation();
  1871. if ( pTargetWinStation == NULL ) {
  1872. TRACE((hTrace, TC_ICASRV, TT_ERROR,
  1873. "TERMSRV: Could not get an idle WinStation!\n"));
  1874. goto releaseresources;
  1875. }
  1876. }
  1877. ASSERT( pTargetWinStation->Flags & WSF_IDLE );
  1878. ASSERT( pTargetWinStation->WinStationName[0] == UNICODE_NULL );
  1879. ASSERT( pTargetWinStation->ConnectEvent );
  1880. if ( pListenWinStation ) {
  1881. pConfig = &(pListenWinStation->Config);
  1882. pListenName = pListenWinStation->WinStationName;
  1883. } else {
  1884. //
  1885. // For Whistler, callback is only for Salem, we can pick
  1886. // configuration from any listening winstation as
  1887. // 1) All we need is HelpAssistant logon/shadow right, which
  1888. // is already in default.
  1889. // 2) No listening winstation, system is either no pending help
  1890. // or not allow to get help, so we need to bail out.
  1891. // 3) Additional check at the bottom to make sure login
  1892. // from callback is HelpAssistant only.
  1893. //
  1894. // If we going support this for the general case, we need
  1895. // to take a default configuration, make connection and issue
  1896. // a new IOCTL call into tdtcp.sys to determine NIC card/IP address
  1897. // that establish connection and from there, map to right winstation
  1898. // configuration.
  1899. //
  1900. // Setup initial callback configuration, this is only
  1901. // heruristic, we will reset the configuration after
  1902. // determine which NIC is used to connect to TS client
  1903. //
  1904. bCanCallout = FindFirstListeningWinStationName(
  1905. szDefaultConfigWinstationName,
  1906. &pTargetWinStation->Config
  1907. );
  1908. if( FALSE == bCanCallout ) {
  1909. // If no listening thread, connection is not active, don't allow
  1910. // callback
  1911. Status = STATUS_ACCESS_DENIED;
  1912. // It's ok to go to releaseresources even if pConfig is not set
  1913. // because in this case pListenWinStation and pEndpoint are NULL.
  1914. goto releaseresources;
  1915. }
  1916. pListenName = szDefaultConfigWinstationName;
  1917. pConfig = &(pTargetWinStation->Config);
  1918. }
  1919. /*
  1920. * Check for MaxInstance
  1921. */
  1922. if ( !(pConfig->Pd[0].Create.PdFlag & PD_SINGLE_INST) ) {
  1923. ULONG Count;
  1924. Count = CountWinStationType( pListenName, FALSE, FALSE );
  1925. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: Count %d\n", Count ));
  1926. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: MaxInstanceCount %d\n",
  1927. pConfig->Create.MaxInstanceCount));
  1928. if ( pConfig->Create.MaxInstanceCount <= Count ) {
  1929. TRACE((hTrace, TC_ICASRV, TT_ERROR,
  1930. "TERMSRV: Exceeded maximum instance count [%ld >= %ld]\n",
  1931. Count, pConfig->Create.MaxInstanceCount));
  1932. goto releaseresources;
  1933. }
  1934. }
  1935. /*
  1936. * Copy the listen name to the target WinStation.
  1937. * This is done to enable tracing on the new stack.
  1938. *
  1939. * Also, this is done BEFORE the connection accept so that if the
  1940. * listen WinStation is reset before the accept completes, the
  1941. * target WinStation will also be reset.
  1942. */
  1943. RtlCopyMemory( pTargetWinStation->ListenName,
  1944. pListenName,
  1945. sizeof(pTargetWinStation->ListenName) );
  1946. /*
  1947. * Enable trace
  1948. */
  1949. RtlZeroMemory( &Trace , sizeof( ICA_TRACE ) );
  1950. InitializeTrace( pTargetWinStation, FALSE, &Trace );
  1951. /*
  1952. * Hook extensions for this target
  1953. */
  1954. pTargetWinStation->pWsx = FindWinStationExtensionDll(
  1955. pConfig->Wd.WsxDLL,
  1956. pConfig->Wd.WdFlag );
  1957. /*
  1958. * Initialize winstation extension context structure
  1959. */
  1960. if (pTargetWinStation->pWsx &&
  1961. pTargetWinStation->pWsx->pWsxWinStationInitialize) {
  1962. Status = pTargetWinStation->pWsx->pWsxWinStationInitialize(
  1963. &pTargetWinStation->pWsxContext);
  1964. if (Status != STATUS_SUCCESS) {
  1965. TRACE((hTrace, TC_ICASRV, TT_ERROR,
  1966. "TERMSRV: WsxWinStationInitialize failed [%lx]\n",
  1967. Status));
  1968. goto badconnect;
  1969. }
  1970. }
  1971. /*
  1972. * Terminate Listen stack of single-instance transports now, so that
  1973. * the underlying CancelIo doesn't disturb the Accept stack.
  1974. */
  1975. // this one can prevent from generalizing efficiently the transfer function
  1976. if (pListenWinStation && (pListenWinStation->Config.Pd[0].Create.PdFlag & PD_SINGLE_INST)) {
  1977. IcaStackTerminate(pListenWinStation->hStack);
  1978. }
  1979. /*
  1980. * Change state to ConnectQuery while we try to accept the connection
  1981. */
  1982. pTargetWinStation->State = State_ConnectQuery;
  1983. NotifySystemEvent(WEVENT_STATECHANGE);
  1984. /*
  1985. * Since the ConnectionAccept may take a while, we have to unlock
  1986. * the target WinStation before the call. However, we set the
  1987. * WSF_IDLEBUSY flag so that the WinStation does not appear idle.
  1988. */
  1989. pTargetWinStation->Flags |= WSF_IDLEBUSY;
  1990. UnlockWinStation( pTargetWinStation );
  1991. if ( !pListenWinStation && pStackAddress) {
  1992. // must have extension DLL loaded
  1993. if( !pTargetWinStation->pWsx || !pTargetWinStation->pWsx->pWsxIcaStackIoControl ) {
  1994. Status = STATUS_UNSUCCESSFUL;
  1995. goto badconnect;
  1996. }
  1997. //
  1998. // Allocate an endpoint buffer
  1999. //
  2000. EndpointLength = MODULE_SIZE;
  2001. pEndpoint = MemAlloc( MODULE_SIZE );
  2002. if ( !pEndpoint ) {
  2003. Status = STATUS_NO_MEMORY;
  2004. goto badconnect;
  2005. }
  2006. Status = IcaStackConnectionRequest( pTargetWinStation->hStack,
  2007. pTargetWinStation->ListenName,
  2008. pConfig,
  2009. pStackAddress,
  2010. pEndpoint,
  2011. EndpointLength,
  2012. &ReturnLength );
  2013. if ( Status == STATUS_BUFFER_TOO_SMALL ) {
  2014. MemFree( pEndpoint );
  2015. pEndpoint = MemAlloc( ReturnLength );
  2016. if ( !pEndpoint ) {
  2017. Status = STATUS_NO_MEMORY;
  2018. goto badconnect;
  2019. }
  2020. EndpointLength = ReturnLength;
  2021. Status = IcaStackConnectionRequest( pTargetWinStation->hStack,
  2022. pTargetWinStation->ListenName,
  2023. pConfig,
  2024. pStackAddress,
  2025. pEndpoint,
  2026. EndpointLength,
  2027. &ReturnLength );
  2028. }
  2029. if ( !NT_SUCCESS(Status) ) {
  2030. // special error code to pass back to client
  2031. StatusCallback = Status;
  2032. goto badconnect;
  2033. }
  2034. }
  2035. /*
  2036. * Now accept the connection for the target WinStation
  2037. * using the new endpoint.
  2038. */
  2039. Status = IcaStackConnectionAccept(pTargetWinStation->hIca,
  2040. pTargetWinStation->hStack,
  2041. pListenName,
  2042. pConfig,
  2043. pEndpoint,
  2044. EndpointLength,
  2045. NULL,
  2046. 0,
  2047. &Trace);
  2048. ConnectionAccepted = (Status == STATUS_SUCCESS);
  2049. TRACE((hTrace,TC_ICASRV,TT_API1,
  2050. "TERMSRV: IcaStackConnectionAccept, LogonId=%d, Status=0x%x\n",
  2051. pTargetWinStation->LogonId, Status));
  2052. if (NT_SUCCESS(Status)) {
  2053. // In post-logon SD work, quering load balancing info has been moved
  2054. // to WinStationNotifyLogonWorker
  2055. // while this part of getting load balancing info is still here
  2056. // because we rely on it to connect to the console
  2057. TS_LOAD_BALANCE_INFO LBInfo;
  2058. ULONG ReturnLength;
  2059. // Get the client load balance capability info.
  2060. // Note we use _IcaStackIoControl() instead of IcaStackIoControl() or
  2061. // WsxIcaStackIoControl() since we still have the stack lock.
  2062. memset(&LBInfo, 0, sizeof(LBInfo));
  2063. Status = _IcaStackIoControl(pTargetWinStation->hStack,
  2064. IOCTL_TS_STACK_QUERY_LOAD_BALANCE_INFO,
  2065. NULL, 0,
  2066. &LBInfo, sizeof(LBInfo),
  2067. &ReturnLength);
  2068. // On non-success, we'll have FALSE for all of our entries, on
  2069. // success valid values. So, save off the cluster info into the
  2070. // WinStation struct now.
  2071. pTargetWinStation->bClientSupportsRedirection =
  2072. LBInfo.bClientSupportsRedirection;
  2073. pTargetWinStation->bRequestedSessionIDFieldValid =
  2074. LBInfo.bRequestedSessionIDFieldValid;
  2075. pTargetWinStation->bClientRequireServerAddr =
  2076. LBInfo.bClientRequireServerAddr;
  2077. pTargetWinStation->RequestedSessionID = LBInfo.RequestedSessionID;
  2078. /*
  2079. * Attempt to license the client. Failure is fatal, do not let the
  2080. * connection continue.
  2081. */
  2082. LCAssignPolicy(pTargetWinStation);
  2083. Status = LCProcessConnectionProtocol(pTargetWinStation);
  2084. TRACE((hTrace,TC_ICASRV,TT_API1,
  2085. "TERMSRV: LCProcessConnectionProtocol, LogonId=%d, Status=0x%x\n",
  2086. pTargetWinStation->LogonId, Status));
  2087. // The stack was locked from successful IcaStackConnectionAccept(),
  2088. // unlock it now.
  2089. IcaStackUnlock(pTargetWinStation->hStack);
  2090. }
  2091. /*
  2092. * Now relock the target WinStation and clear the IDLEBUSY flag.
  2093. */
  2094. rc = RelockWinStation(pTargetWinStation);
  2095. pTargetWinStation->Flags &= ~WSF_IDLEBUSY;
  2096. /*
  2097. * If the connection accept was not successful,
  2098. * then we have no choice but to close the connection endpoint
  2099. * and go back and wait for another connection.
  2100. */
  2101. if (!NT_SUCCESS(Status) || !rc) {
  2102. TRACE((hTrace, TC_ICASRV, TT_ERROR,
  2103. "TERMSRV: Connection attempt failed, Status [%lx], rc [%lx]\n",
  2104. Status, rc));
  2105. goto badconnect;
  2106. }
  2107. /*
  2108. * The connection accept was successful, save the
  2109. * new endpoint in the target WinStation, copy the config
  2110. * parameters to the target WinStation, and reset the WSF_IDLE flag.
  2111. */
  2112. pTargetWinStation->pEndpoint = pEndpoint;
  2113. pTargetWinStation->EndpointLength = EndpointLength;
  2114. if ( pListenWinStation )
  2115. pTargetWinStation->Config = pListenWinStation->Config;
  2116. pTargetWinStation->Flags &= ~WSF_IDLE;
  2117. /*
  2118. * Copy real name of Single-Instance transports
  2119. */
  2120. if ( pConfig->Pd[0].Create.PdFlag & PD_SINGLE_INST ) {
  2121. RtlCopyMemory( pTargetWinStation->WinStationName,
  2122. pTargetWinStation->ListenName,
  2123. sizeof(pTargetWinStation->WinStationName) );
  2124. /*
  2125. * Otherwise, build dynamic name from Listen name and target LogonId.
  2126. */
  2127. } else {
  2128. int CopyCount;
  2129. WINSTATIONNAME TempName;
  2130. // swprintf( TempName, L"#%d", pTargetWinStation->LogonId );
  2131. ASSERT(pTargetWinStation->LogonId > 0 && pTargetWinStation->LogonId < 65536);
  2132. swprintf( TempName, L"#%d", pTargetWinStation->SessionSerialNumber );
  2133. CopyCount = min( wcslen( pTargetWinStation->ListenName ),
  2134. sizeof( pTargetWinStation->WinStationName ) /
  2135. sizeof( pTargetWinStation->WinStationName[0] ) -
  2136. wcslen( TempName ) - 1 );
  2137. wcsncpy( pTargetWinStation->WinStationName,
  2138. pTargetWinStation->ListenName,
  2139. CopyCount );
  2140. wcscpy( &pTargetWinStation->WinStationName[CopyCount], TempName );
  2141. }
  2142. /*
  2143. * Inherit the security descriptor from the listen WINSTATION to the
  2144. * connected WINSTATION.
  2145. */
  2146. if ( pListenWinStation ) {
  2147. RtlAcquireResourceShared(&WinStationSecurityLock, TRUE);
  2148. Status = WinStationInheritSecurityDescriptor( pListenWinStation->pSecurityDescriptor,
  2149. pTargetWinStation );
  2150. if (Status != STATUS_SUCCESS) {
  2151. // badconnect free pEndpoint, WinStationTerminate() will try to free this
  2152. // end point again causing double free.
  2153. pTargetWinStation->pEndpoint = NULL;
  2154. goto badconnect;
  2155. }
  2156. RtlReleaseResource(&WinStationSecurityLock);
  2157. } else {
  2158. ReadWinStationSecurityDescriptor( pTargetWinStation );
  2159. }
  2160. /*
  2161. * Initialize client data
  2162. */
  2163. pTargetWinStation->Client.ClientSessionId = LOGONID_NONE;
  2164. ZeroMemory( pTargetWinStation->Client.clientDigProductId, sizeof( pTargetWinStation->Client.clientDigProductId ));
  2165. pTargetWinStation->Client.PerformanceFlags = TS_PERF_DISABLE_NOTHING;
  2166. if ( pTargetWinStation->pWsx && pTargetWinStation->pWsx->pWsxIcaStackIoControl ) {
  2167. (void) pTargetWinStation->pWsx->pWsxIcaStackIoControl(
  2168. pTargetWinStation->pWsxContext,
  2169. pTargetWinStation->hIca,
  2170. pTargetWinStation->hStack,
  2171. IOCTL_ICA_STACK_QUERY_CLIENT,
  2172. NULL,
  2173. 0,
  2174. &pTargetWinStation->Client,
  2175. sizeof(pTargetWinStation->Client),
  2176. &ReturnLength );
  2177. }
  2178. if ( pTargetWinStation->Config.Config.User.fWallPaperDisabled )
  2179. {
  2180. pTargetWinStation->Client.PerformanceFlags |= TS_PERF_DISABLE_WALLPAPER;
  2181. }
  2182. //
  2183. // Clear helpassistant specific bits to indicate we still not sure
  2184. // login user is a help assistant
  2185. //
  2186. pTargetWinStation->StateFlags &= ~WSF_ST_HELPSESSION_FLAGS;
  2187. bSessionIsHelpSession = TSIsSessionHelpSession( pTargetWinStation, &bValidRAConnect );
  2188. //
  2189. // If TS policy denies connection, only way to comes to
  2190. // here is policy allow help, reject connection if logon
  2191. // user is not salem help assistant.
  2192. //
  2193. //
  2194. // Disconnect client on following
  2195. //
  2196. // 1) Safeboot with networking if not RA connect.
  2197. // 2) Reverse connection if not RA connect.
  2198. // 3) TS not accepting connection via policy setting if not RA connect.
  2199. // 4) Not allowing help if RA connect.
  2200. // 5) Invalid RA connection if RA connect.
  2201. //
  2202. if( TRUE == bSessionIsHelpSession )
  2203. {
  2204. //
  2205. // If invalid ticket or policy deny help, we immediately disconnect
  2206. //
  2207. if( FALSE == bValidRAConnect || FALSE == bPolicyAllowHelp )
  2208. {
  2209. // Invalid ticket, disconnect immediately
  2210. TRACE((hTrace, TC_ICASRV, TT_ERROR,
  2211. "TERMSRV: Invalid RA login\n"));
  2212. goto invalid_ra_connection;
  2213. }
  2214. }
  2215. else if( !pListenWinStation && pStackAddress )
  2216. {
  2217. //
  2218. // Reverse Connect, parameter passed in pListenWinStation = NULL
  2219. // and pStackAddress is not NULL, for normal connection,
  2220. // pListenWinStation is not NULL but pStackAddress is NULL
  2221. //
  2222. //
  2223. // Handle non-RA Reverse connection, Whistler revert connection
  2224. // only allow RA login.
  2225. //
  2226. TRACE((hTrace, TC_ICASRV, TT_ERROR,
  2227. "TERMSRV: Not/invalid Help Assistant logon\n"));
  2228. goto invalid_ra_connection;
  2229. }
  2230. //
  2231. // Connecting client must be either non-RA or valid RA connection.
  2232. //
  2233. //
  2234. // Handle Safeboot with networking and TS deny non-RA connection,
  2235. // safeboot with networking only allow RA connection.
  2236. //
  2237. if( g_SafeBootWithNetwork || g_fDenyTSConnectionsPolicy || g_bPersonalWks)
  2238. {
  2239. if( FALSE == bSessionIsHelpSession )
  2240. {
  2241. TRACE((hTrace, TC_ICASRV, TT_ERROR,
  2242. "TERMSRV: Policy or safeboot denied connection\n"));
  2243. goto invalid_ra_connection;
  2244. }
  2245. }
  2246. //
  2247. // Only log Salem event for reverse connection
  2248. //
  2249. if( !pListenWinStation && pStackAddress )
  2250. {
  2251. ASSERT( TRUE == bSessionIsHelpSession );
  2252. TSLogSalemReverseConnection(pTargetWinStation, pStackAddress);
  2253. }
  2254. /*
  2255. * Set the connect event to wake up the target WinStation.
  2256. */
  2257. if (pTargetWinStation->ConnectEvent != NULL) {
  2258. NtSetEvent( pTargetWinStation->ConnectEvent, NULL );
  2259. }
  2260. /*
  2261. * Release target WinStation
  2262. */
  2263. if( pListenWinStation )
  2264. {
  2265. if (bSuccessAdded) { // If we could add this IP address to the per IP list then remember it.
  2266. PREMEMBERED_CLIENT_ADDRESS pAddress;
  2267. if ((uAddrSize != 0) && (pAddress = (PREMEMBERED_CLIENT_ADDRESS) MemAlloc( sizeof(REMEMBERED_CLIENT_ADDRESS) + uAddrSize -1 ))!= NULL ) {
  2268. pAddress->length = uAddrSize;
  2269. RtlCopyMemory( &pAddress->addr[0] , in_addr,uAddrSize );
  2270. pTargetWinStation->pRememberedAddress = pAddress;
  2271. } else {
  2272. Filter_RemoveOutstandingConnection( in_addr, uAddrSize );
  2273. if( (ULONG)InterlockedDecrement( &NumOutStandingConnect ) == MaxOutStandingConnect )
  2274. {
  2275. if (hConnectEvent != NULL)
  2276. {
  2277. SetEvent(hConnectEvent);
  2278. }
  2279. }
  2280. }
  2281. } else{ // We could not add this IP address to the pr IP list
  2282. if( (ULONG)InterlockedDecrement( &NumOutStandingConnect ) == MaxOutStandingConnect )
  2283. {
  2284. if (hConnectEvent != NULL)
  2285. {
  2286. SetEvent(hConnectEvent);
  2287. }
  2288. }
  2289. }
  2290. }
  2291. ReleaseWinStation( pTargetWinStation );
  2292. /*
  2293. * If necessary, create another idle WinStation to replace the one being connected
  2294. */
  2295. NtSetEvent(WinStationIdleControlEvent, NULL);
  2296. return STATUS_SUCCESS;
  2297. /*=============================================================================
  2298. == Error returns
  2299. =============================================================================*/
  2300. invalid_ra_connection:
  2301. // badconnect free pEndpoint, WinStationTerminate() will try to free this
  2302. // end point again causing double free.
  2303. pTargetWinStation->pEndpoint = NULL;
  2304. StatusCallback = STATUS_CTX_WINSTATION_ACCESS_DENIED;
  2305. /*
  2306. * Error during ConnectionAccept
  2307. */
  2308. badconnect:
  2309. /*
  2310. * Clear the Listen name
  2311. */
  2312. RtlZeroMemory( pTargetWinStation->ListenName,
  2313. sizeof(pTargetWinStation->ListenName) );
  2314. /*
  2315. * Call WinStation rundown function before killing the stack
  2316. */
  2317. if (pTargetWinStation->pWsxContext) {
  2318. if ( pTargetWinStation->pWsx &&
  2319. pTargetWinStation->pWsx->pWsxWinStationRundown ) {
  2320. pTargetWinStation->pWsx->pWsxWinStationRundown( pTargetWinStation->pWsxContext );
  2321. }
  2322. pTargetWinStation->pWsxContext = NULL;
  2323. }
  2324. pTargetWinStation->pWsx = NULL;
  2325. /*
  2326. * Release system resources. This happens in two phases:
  2327. *
  2328. * a.) The connection endpoint - since endpoints are not reference counted
  2329. * care must be taken to destroy the endpoint with the stack in which it
  2330. * was loaded. In the event it was not loaded, we create a temporary
  2331. * stack to do the dirty work.
  2332. *
  2333. * b.) The Winstation inself - if we had to create an idle winstation to
  2334. * handle this connection, it is destroyed. Otherwise we just return
  2335. * it back to the idle pool.
  2336. *
  2337. */
  2338. releaseresources:
  2339. /*
  2340. * If we created a target WinStation, then use its stack to close the
  2341. * endpoint since the stack may have a reference to it.
  2342. */
  2343. TRACE((hTrace, TC_ICASRV, TT_ERROR,
  2344. "TERMSRV: Closing Endpoint [0x%p], winsta = 0x%p, Accepted = %ld\n",
  2345. pEndpoint, pTargetWinStation, ConnectionAccepted));
  2346. if ((pTargetWinStation != NULL) && (ConnectionAccepted)) {
  2347. Status = _CloseEndpoint( pConfig,
  2348. pEndpoint,
  2349. EndpointLength,
  2350. pTargetWinStation,
  2351. FALSE ); // Use the stack which already has
  2352. // the endpoint loaded
  2353. }
  2354. /*
  2355. * Otherwise, we failed before we got the endpoint loaded so close the
  2356. * endpoint using a temporary stack.
  2357. */
  2358. else if ( pListenWinStation ) {
  2359. // note that:
  2360. // 1. if pListenWinStation is NULL then pEndpoint is NULL, so nothing to close;
  2361. // 2. use the config of pListenWinStation in case pConfig is not set yet.
  2362. Status = _CloseEndpoint( &pListenWinStation->Config,
  2363. pEndpoint,
  2364. EndpointLength,
  2365. pListenWinStation,
  2366. TRUE ); // Use a temporary stack
  2367. }
  2368. if ( pEndpoint )
  2369. MemFree( pEndpoint );
  2370. pEndpoint = NULL;
  2371. /*
  2372. * Return the winstation if we got that far in the protocol sequence
  2373. */
  2374. if (pTargetWinStation != NULL) {
  2375. /*
  2376. * If we created a WinStation above because there were no IDLE
  2377. * WinStations available, then we will now have an extra IDLE
  2378. * WinStation. In this case, reset the current IDLE WinStation.
  2379. */
  2380. if ( CreatedIdle ) {
  2381. QueueWinStationReset( pTargetWinStation->LogonId);
  2382. // clear this so it doesn't get selected as idle when unlocked
  2383. pTargetWinStation->Flags &= ~WSF_IDLE;
  2384. ReleaseWinStation( pTargetWinStation );
  2385. }
  2386. /*
  2387. * Else return this WinStation to the idle pool after cleaning up the
  2388. * stack.
  2389. */
  2390. else {
  2391. //
  2392. // The licensing context needs to be freed and recreated to
  2393. // ensure it is cleaned up properly.
  2394. //
  2395. LCDestroyContext(pTargetWinStation);
  2396. Status = LCCreateContext(pTargetWinStation);
  2397. if (NT_SUCCESS(Status))
  2398. {
  2399. Status = IcaStackClose( pTargetWinStation->hStack );
  2400. ASSERT( NT_SUCCESS( Status ) );
  2401. Status = IcaStackOpen( pTargetWinStation->hIca,
  2402. Stack_Primary,
  2403. (PROC)WsxStackIoControl,
  2404. pTargetWinStation,
  2405. &pTargetWinStation->hStack );
  2406. }
  2407. if (NT_SUCCESS(Status)) {
  2408. pTargetWinStation->State = State_Idle;
  2409. NotifySystemEvent( WEVENT_STATECHANGE );
  2410. ReleaseWinStation( pTargetWinStation );
  2411. } else {
  2412. pTargetWinStation->Flags &= ~WSF_IDLE;
  2413. QueueWinStationReset( pTargetWinStation->LogonId);
  2414. ReleaseWinStation( pTargetWinStation );
  2415. }
  2416. }
  2417. }
  2418. if ( pListenWinStation )
  2419. {
  2420. if (bSuccessAdded) {
  2421. Filter_RemoveOutstandingConnection( in_addr, uAddrSize );
  2422. }
  2423. if( (ULONG)InterlockedDecrement( &NumOutStandingConnect ) == MaxOutStandingConnect )
  2424. {
  2425. if (hConnectEvent != NULL)
  2426. {
  2427. SetEvent(hConnectEvent);
  2428. }
  2429. }
  2430. }
  2431. // If error is due to call back, return meaningful error
  2432. // code.
  2433. if( STATUS_SUCCESS != StatusCallback )
  2434. {
  2435. return StatusCallback;
  2436. }
  2437. return -1 /*STATUS_CTX_UNACCEPTED_CONNECTION*/;
  2438. }
  2439. /*******************************************************************************
  2440. * ConnectSmWinStationApiPort
  2441. *
  2442. * Open a connection to the WinStationApiPort. This will be used
  2443. * to queue requests to the Api Request thread instead of processing
  2444. * them in line.
  2445. ******************************************************************************/
  2446. NTSTATUS ConnectSmWinStationApiPort()
  2447. {
  2448. UNICODE_STRING PortName;
  2449. SECURITY_QUALITY_OF_SERVICE DynamicQos;
  2450. WINSTATIONAPI_CONNECT_INFO info;
  2451. ULONG ConnectInfoLength;
  2452. NTSTATUS Status;
  2453. /*
  2454. * Set up the security quality of service parameters to use over the
  2455. * port. Use the most efficient (least overhead) - which is dynamic
  2456. * rather than static tracking.
  2457. */
  2458. DynamicQos.ImpersonationLevel = SecurityImpersonation;
  2459. DynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
  2460. DynamicQos.EffectiveOnly = TRUE;
  2461. RtlInitUnicodeString( &PortName, L"\\SmSsWinStationApiPort" );
  2462. // Fill in the ConnectInfo structure with our access request mask
  2463. info.Version = CITRIX_WINSTATIONAPI_VERSION;
  2464. info.RequestedAccess = 0;
  2465. ConnectInfoLength = sizeof(WINSTATIONAPI_CONNECT_INFO);
  2466. Status = NtConnectPort( &WinStationApiPort,
  2467. &PortName,
  2468. &DynamicQos,
  2469. NULL,
  2470. NULL,
  2471. NULL, // Max message length [select default]
  2472. (PVOID)&info,
  2473. &ConnectInfoLength );
  2474. if ( !NT_SUCCESS( Status ) ) {
  2475. // Look at the returned INFO to see why if desired
  2476. if ( ConnectInfoLength == sizeof(WINSTATIONAPI_CONNECT_INFO) ) {
  2477. DBGPRINT(( "TERMSRV: Sm connect failed, Reason 0x%x\n",
  2478. info.AcceptStatus));
  2479. }
  2480. DBGPRINT(( "TERMSRV: Connect to SM failed %lx\n", Status ));
  2481. }
  2482. return Status;
  2483. }
  2484. /*******************************************************************************
  2485. * QueueWinStationCreate
  2486. *
  2487. * Send a create message to the WinStationApiPort.
  2488. *
  2489. * ENTRY:
  2490. * pWinStationName (input)
  2491. * Pointer to WinStationName to be created
  2492. ******************************************************************************/
  2493. NTSTATUS QueueWinStationCreate(PWINSTATIONNAME pWinStationName)
  2494. {
  2495. WINSTATION_APIMSG msg;
  2496. NTSTATUS Status;
  2497. TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: QueueWinStationCreate: %S\n", pWinStationName ));
  2498. /*
  2499. * Initialize msg
  2500. */
  2501. msg.h.u1.s1.DataLength = sizeof(msg) - sizeof(PORT_MESSAGE);
  2502. msg.h.u1.s1.TotalLength = sizeof(msg);
  2503. msg.h.u2.s2.Type = 0; // Kernel will fill in message type
  2504. msg.h.u2.s2.DataInfoOffset = 0;
  2505. msg.ApiNumber = SMWinStationCreate;
  2506. msg.WaitForReply = FALSE;
  2507. if ( pWinStationName ) {
  2508. RtlCopyMemory( msg.u.Create.WinStationName, pWinStationName,
  2509. sizeof(msg.u.Create.WinStationName) );
  2510. } else {
  2511. RtlZeroMemory( msg.u.Create.WinStationName,
  2512. sizeof(msg.u.Create.WinStationName) );
  2513. }
  2514. /*
  2515. * Send create message to our API request thread
  2516. * but don't wait for a reply.
  2517. */
  2518. Status = NtRequestPort( WinStationApiPort, (PPORT_MESSAGE) &msg );
  2519. ASSERT( NT_SUCCESS( Status ) );
  2520. return Status;
  2521. }
  2522. /*******************************************************************************
  2523. * QueueWinStationReset
  2524. *
  2525. * Send a reset message to the WinStationApiPort.
  2526. *
  2527. * ENTRY:
  2528. * LogonId (input)
  2529. * LogonId of WinStationName to reset
  2530. ******************************************************************************/
  2531. NTSTATUS QueueWinStationReset(ULONG LogonId)
  2532. {
  2533. WINSTATION_APIMSG msg;
  2534. NTSTATUS Status;
  2535. TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: QueueWinStationReset: %u\n", LogonId ));
  2536. /*
  2537. * Initialize msg
  2538. */
  2539. msg.h.u1.s1.DataLength = sizeof(msg) - sizeof(PORT_MESSAGE);
  2540. msg.h.u1.s1.TotalLength = sizeof(msg);
  2541. msg.h.u2.s2.Type = 0; // Kernel will fill in message type
  2542. msg.h.u2.s2.DataInfoOffset = 0;
  2543. msg.ApiNumber = SMWinStationReset;
  2544. msg.WaitForReply = FALSE;
  2545. msg.u.Reset.LogonId = LogonId;
  2546. /*
  2547. * Send reset message to our API request thread
  2548. * but don't wait for a reply.
  2549. */
  2550. Status = NtRequestPort( WinStationApiPort, (PPORT_MESSAGE) &msg );
  2551. ASSERT( NT_SUCCESS( Status ) );
  2552. return( Status );
  2553. }
  2554. /*******************************************************************************
  2555. * QueueWinStationDisconnect
  2556. *
  2557. * Send a disconnect message to the WinStationApiPort.
  2558. *
  2559. * ENTRY:
  2560. * LogonId (input)
  2561. * LogonId of WinStationName to disconnect
  2562. ******************************************************************************/
  2563. NTSTATUS QueueWinStationDisconnect(ULONG LogonId)
  2564. {
  2565. WINSTATION_APIMSG msg;
  2566. NTSTATUS Status;
  2567. TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: QueueWinStationDisconnect: %u\n", LogonId ));
  2568. /*
  2569. * Initialize msg
  2570. */
  2571. msg.h.u1.s1.DataLength = sizeof(msg) - sizeof(PORT_MESSAGE);
  2572. msg.h.u1.s1.TotalLength = sizeof(msg);
  2573. msg.h.u2.s2.Type = 0; // Kernel will fill in message type
  2574. msg.h.u2.s2.DataInfoOffset = 0;
  2575. msg.ApiNumber = SMWinStationDisconnect;
  2576. msg.WaitForReply = FALSE;
  2577. msg.u.Reset.LogonId = LogonId;
  2578. /*
  2579. * Send disconnect message to our API request thread
  2580. * but don't wait for a reply.
  2581. */
  2582. Status = NtRequestPort( WinStationApiPort, (PPORT_MESSAGE) &msg );
  2583. ASSERT( NT_SUCCESS( Status ) );
  2584. return( Status );
  2585. }
  2586. /*******************************************************************************
  2587. * IcaRegWinStationEnumerate
  2588. *
  2589. * Enumerate all WinStations configured in the registry.
  2590. *
  2591. * ENTRY:
  2592. * pWinStationCount (input/output)
  2593. * count of WinStation names to return, on return the number of
  2594. * WinStation names actually returned in name buffer
  2595. * pWinStationName (output)
  2596. * pointer to buffer to return WinStation names
  2597. * pByteCount (input/output)
  2598. * size of WinStation name buffer, on return the number of
  2599. * bytes actually returned in the name buffer
  2600. ******************************************************************************/
  2601. NTSTATUS IcaRegWinStationEnumerate(
  2602. PULONG pWinStationCount,
  2603. PWINSTATIONNAME pWinStationName,
  2604. PULONG pByteCount)
  2605. {
  2606. NTSTATUS Status;
  2607. OBJECT_ATTRIBUTES ObjectAttributes;
  2608. WCHAR PathBuffer[ 260 ];
  2609. UNICODE_STRING KeyPath;
  2610. HANDLE Handle;
  2611. ULONG i;
  2612. ULONG Count;
  2613. wcscpy( PathBuffer, REG_NTAPI_CONTROL_TSERVER L"\\" REG_WINSTATIONS );
  2614. RtlInitUnicodeString( &KeyPath, PathBuffer );
  2615. InitializeObjectAttributes( &ObjectAttributes, &KeyPath,
  2616. OBJ_CASE_INSENSITIVE, NULL, NULL );
  2617. Status = NtOpenKey( &Handle, GENERIC_READ, &ObjectAttributes );
  2618. if ( !NT_SUCCESS( Status ) ) {
  2619. DBGPRINT(( "TERMSRV: NtOpenKey failed, rc=%x\n", Status ));
  2620. return( Status );
  2621. }
  2622. Count = pWinStationName ?
  2623. min( *pByteCount / sizeof(WINSTATIONNAME), *pWinStationCount ) :
  2624. (ULONG) -1;
  2625. *pWinStationCount = *pByteCount = 0;
  2626. for ( i = 0; i < Count; i++ ) {
  2627. WINSTATIONNAME WinStationName;
  2628. UNICODE_STRING WinStationString;
  2629. WinStationString.Length = 0;
  2630. WinStationString.MaximumLength = sizeof(WinStationName);
  2631. WinStationString.Buffer = WinStationName;
  2632. Status = RtlpNtEnumerateSubKey( Handle, &WinStationString, i, NULL );
  2633. if ( !NT_SUCCESS( Status ) ) {
  2634. if ( Status != STATUS_NO_MORE_ENTRIES ) {
  2635. DBGPRINT(( "TERMSRV: RtlpNtEnumerateSubKey failed, rc=%x\n", Status ));
  2636. }
  2637. break;
  2638. }
  2639. if ( pWinStationName ) {
  2640. RtlCopyMemory( pWinStationName, WinStationName,
  2641. WinStationString.Length );
  2642. pWinStationName[WinStationString.Length>>1] = UNICODE_NULL;
  2643. (char*)pWinStationName += sizeof(WINSTATIONNAME);
  2644. }
  2645. (*pWinStationCount)++;
  2646. *pByteCount += sizeof(WINSTATIONNAME);
  2647. }
  2648. NtClose( Handle );
  2649. return STATUS_SUCCESS;
  2650. }
  2651. /*******************************************************************************
  2652. * WinStationCreateWorker
  2653. *
  2654. * Worker routine to create/start a WinStation.
  2655. *
  2656. * ENTRY:
  2657. * pWinStationName (input) (optional)
  2658. * Pointer to WinStationName to be created
  2659. * pLogonId (output)
  2660. * location to return LogonId of new WinStation
  2661. *
  2662. * NOTE: If a WinStation name is specified, then this will become the
  2663. * "listening" WinStation for the specified name.
  2664. * If a WinStation name is not specified, then this will become
  2665. * part of the free pool of idle/disconnected WinStations.
  2666. ******************************************************************************/
  2667. NTSTATUS WinStationCreateWorker(
  2668. PWINSTATIONNAME pWinStationName,
  2669. PULONG pLogonId )
  2670. {
  2671. BOOL fConsole;
  2672. PWINSTATION pWinStation;
  2673. PWINSTATION pCurWinStation;
  2674. NTSTATUS Status;
  2675. UNICODE_STRING WinStationString;
  2676. ULONG ReturnLength;
  2677. /*
  2678. * If system shutdown is in progress, then don't allow create
  2679. */
  2680. if ( ShutdownInProgress ) {
  2681. Status = STATUS_ACCESS_DENIED;
  2682. goto shutdown;
  2683. }
  2684. if (pWinStationName == NULL)
  2685. {
  2686. fConsole = FALSE;
  2687. }
  2688. else
  2689. {
  2690. fConsole = (_wcsicmp(pWinStationName, L"Console") == 0);
  2691. }
  2692. /*
  2693. * If not the Console, then verify the WinStation name is defined
  2694. * in the registry and that it is enabled.
  2695. */
  2696. if ( pWinStationName && !fConsole ) {
  2697. Status = CheckWinStationEnable( pWinStationName );
  2698. if ( Status != STATUS_SUCCESS ) {
  2699. DBGPRINT(( "TERMSRV: WinStation '%ws' is disabled\n", pWinStationName ));
  2700. goto disabled;
  2701. }
  2702. }
  2703. /*
  2704. * Allocate and initialize WinStation struct
  2705. */
  2706. if ( (pWinStation = MemAlloc( sizeof(WINSTATION) )) == NULL ) {
  2707. Status = STATUS_NO_MEMORY;
  2708. goto nomem;
  2709. }
  2710. RtlZeroMemory( pWinStation, sizeof(WINSTATION) );
  2711. pWinStation->Starting = TRUE;
  2712. pWinStation->NeverConnected = TRUE;
  2713. pWinStation->State = State_Init;
  2714. pWinStation->pNewNotificationCredentials = NULL;
  2715. pWinStation->LastReconnectType = NeverReconnected;
  2716. pWinStation->fDisallowAutoReconnect = FALSE;
  2717. InitializeListHead( &pWinStation->ShadowHead );
  2718. InitializeListHead( &pWinStation->Win32CommandHead );
  2719. // Create the licensing context
  2720. Status = LCCreateContext(pWinStation);
  2721. if ( !NT_SUCCESS( Status ) )
  2722. goto nolicensecontext;
  2723. // Create and lock winstation mutex
  2724. Status = InitRefLock( &pWinStation->Lock, WinStationDeleteProc );
  2725. if ( !NT_SUCCESS( Status ) )
  2726. goto nolock;
  2727. /*
  2728. * If a WinStation name was specified, see if it already exists
  2729. * (on return, WinStationListLock will be locked).
  2730. */
  2731. if ( pWinStationName ) {
  2732. if ( pCurWinStation = FindWinStationByName( pWinStationName, TRUE ) ) {
  2733. ReleaseWinStation( pCurWinStation );
  2734. LEAVECRIT( &WinStationListLock );
  2735. Status = STATUS_CTX_WINSTATION_NAME_COLLISION;
  2736. goto alreadyexists;
  2737. }
  2738. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: Creating WinStation %ws\n", pWinStationName ));
  2739. wcscpy( pWinStation->WinStationName, pWinStationName );
  2740. /*
  2741. * If not the console, then this will become a "listen" WinStation
  2742. */
  2743. if ( !fConsole ) {
  2744. pWinStation->Flags |= WSF_LISTEN;
  2745. //
  2746. // Listener winstations always get LogonId above 65536 and are
  2747. // assigned by Terminal Server. LogonId's for sessions are
  2748. // generated by mm in the range 0-65535
  2749. //
  2750. pWinStation->LogonId = LogonId++;
  2751. ASSERT(pWinStation->LogonId >= 65536);
  2752. } else {
  2753. //
  2754. // Console always get 0
  2755. //
  2756. pWinStation->LogonId = 0;
  2757. pWinStation->fOwnsConsoleTerminal = TRUE;
  2758. }
  2759. /*
  2760. * No WinStation name was specified.
  2761. * This will be an idle WinStation waiting for a connection.
  2762. */
  2763. } else {
  2764. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: Creating IDLE WinStation\n" ));
  2765. pWinStation->Flags |= WSF_IDLE;
  2766. pWinStation->LogonId = -1; // MM will asssign session IDs
  2767. ENTERCRIT( &WinStationListLock );
  2768. }
  2769. /*
  2770. * Allocate LogonId and insert in WinStation list
  2771. */
  2772. InsertTailList( &WinStationListHead, &pWinStation->Links );
  2773. LEAVECRIT( &WinStationListLock );
  2774. /*
  2775. * Initialize WinStation configuration data
  2776. */
  2777. #ifdef NO_CONSOLE_REGISTRY
  2778. if ( pWinStation->LogonId ) {
  2779. #endif
  2780. /*
  2781. * Read winstation configuration data from registry
  2782. */
  2783. if ( pWinStationName ) {
  2784. Status = RegWinStationQueryEx( SERVERNAME_CURRENT,
  2785. &g_MachinePolicy,
  2786. pWinStationName,
  2787. &pWinStation->Config,
  2788. sizeof(WINSTATIONCONFIG2),
  2789. &ReturnLength, TRUE );
  2790. if ( !NT_SUCCESS(Status) ) {
  2791. goto badregdata;
  2792. }
  2793. if (pWinStation->Config.Wd.WdFlag & WDF_TSHARE)
  2794. {
  2795. pWinStation->Client.ProtocolType = PROTOCOL_RDP;
  2796. }
  2797. else if (pWinStation->Config.Wd.WdFlag & WDF_ICA)
  2798. {
  2799. pWinStation->Client.ProtocolType = PROTOCOL_ICA;
  2800. }
  2801. else
  2802. {
  2803. pWinStation->Client.ProtocolType = PROTOCOL_CONSOLE;
  2804. }
  2805. /*
  2806. * Save console config for console sessions.
  2807. */
  2808. if (pWinStation->LogonId == 0) {
  2809. gConsoleConfig = pWinStation->Config;
  2810. // initalize client data, since there isn't any real rdp client sending anythhing to us
  2811. InitializeConsoleClientData( & pWinStation->Client );
  2812. }
  2813. }
  2814. #ifdef NO_CONSOLE_REGISTRY
  2815. } else {
  2816. /*
  2817. * Hand craft the console configuration data
  2818. */
  2819. PWDCONFIG pWdConfig = &pWinStation->Config.Wd;
  2820. PPDCONFIGW pPdConfig = &pWinStation->Config.Pd[0];
  2821. wcscpy( pWdConfig->WdName, L"Console" );
  2822. pWdConfig->WdFlag = WDF_NOT_IN_LIST;
  2823. wcscpy( pPdConfig->Create.PdName, L"Console" );
  2824. pPdConfig->Create.PdClass = PdConsole;
  2825. pPdConfig->Create.PdFlag = PD_USE_WD | PD_RELIABLE | PD_FRAME |
  2826. PD_CONNECTION | PD_CONSOLE;
  2827. RegQueryOEMId( (PBYTE) &pWinStation->Config.Config.OEMId,
  2828. sizeof(pWinStation->Config.Config.OEMId) );
  2829. }
  2830. #endif
  2831. if (pWinStation->LogonId == 0 || g_bPersonalTS) {
  2832. // Create a named event for console session so that winmm can check if we
  2833. // are remoting audio on the console itself. Use a global event is for
  2834. // fast check
  2835. {
  2836. BYTE bSA[SECURITY_DESCRIPTOR_MIN_LENGTH];
  2837. PSECURITY_DESCRIPTOR pSD = &bSA;
  2838. SECURITY_ATTRIBUTES SA;
  2839. EXPLICIT_ACCESS ea;
  2840. SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY;
  2841. PSID pSidWorld;
  2842. PACL pNewDAcl;
  2843. DWORD dwres;
  2844. if ( AllocateAndInitializeSid( &siaWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pSidWorld))
  2845. {
  2846. ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
  2847. ea.grfAccessPermissions = SYNCHRONIZE;
  2848. ea.grfAccessMode = GRANT_ACCESS;
  2849. ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
  2850. ea.Trustee.ptstrName = (LPTSTR)pSidWorld;
  2851. dwres = SetEntriesInAcl(1, &ea, NULL, &pNewDAcl );
  2852. if ( ERROR_SUCCESS == dwres )
  2853. {
  2854. if (InitializeSecurityDescriptor(pSD,
  2855. SECURITY_DESCRIPTOR_REVISION))
  2856. {
  2857. if (SetSecurityDescriptorDacl(pSD, TRUE, pNewDAcl, FALSE ))
  2858. {
  2859. SA.nLength = sizeof( SA );
  2860. SA.lpSecurityDescriptor = pSD;
  2861. SA.bInheritHandle = FALSE;
  2862. pWinStation->hWinmmConsoleAudioEvent =
  2863. CreateEvent( &SA, TRUE, FALSE, L"Global\\WinMMConsoleAudioEvent");
  2864. }
  2865. }
  2866. LocalFree( pNewDAcl );
  2867. }
  2868. LocalFree( pSidWorld );
  2869. }
  2870. }
  2871. }
  2872. else {
  2873. pWinStation->hWinmmConsoleAudioEvent = NULL;
  2874. }
  2875. /*
  2876. * Start the WinStation
  2877. */
  2878. Status = WinStationStart( pWinStation );
  2879. /*
  2880. * Ignore errors from console, otherwise keep going
  2881. */
  2882. if ( ( pWinStation->LogonId ) && ( Status != STATUS_SUCCESS ) )
  2883. goto starterror;
  2884. /*
  2885. * Return LogonId to caller
  2886. */
  2887. if ( pLogonId )
  2888. *pLogonId = pWinStation->LogonId;
  2889. // Increment the total number of sessions created since TermSrv started.
  2890. // we don't count the console and listener sessions
  2891. if (pWinStation->LogonId > 0 && pWinStation->LogonId < 65536) {
  2892. pWinStation->SessionSerialNumber = (ULONG) InterlockedIncrement(&g_TermSrvTotalSessions);
  2893. }
  2894. if (!(pWinStation->Flags & WSF_LISTEN))
  2895. {
  2896. Status = InitializeSessionNotification(pWinStation);
  2897. if ( !NT_SUCCESS( Status ) )
  2898. goto starterror;
  2899. }
  2900. /*
  2901. * Set WinStationEvent to indicate another WinStation has been created
  2902. */
  2903. ENTERCRIT( &WinStationListLock );
  2904. pWinStation->Starting = FALSE;
  2905. NtSetEvent( WinStationEvent, NULL );
  2906. // Keep track of total session count for Load Balancing Indicator but
  2907. // don't count listen winstations
  2908. if (!(pWinStation->Flags & WSF_LISTEN))
  2909. WinStationTotalCount++;
  2910. LEAVECRIT( &WinStationListLock );
  2911. /*
  2912. * Release WinStation now
  2913. */
  2914. ReleaseWinStation( pWinStation );
  2915. /*
  2916. * Notify clients of WinStation create
  2917. */
  2918. NotifySystemEvent( WEVENT_CREATE );
  2919. return( STATUS_SUCCESS );
  2920. /*=============================================================================
  2921. == Error returns
  2922. =============================================================================*/
  2923. /*
  2924. * WinStationStart returned error
  2925. * WinStation kernel object could not be created
  2926. */
  2927. starterror:
  2928. if ( !(pWinStation->Flags & (WSF_RESET | WSF_DELETE)) ) {
  2929. if ( StopOnDown )
  2930. DbgBreakPoint();
  2931. pWinStation->Flags |= WSF_DELETE;
  2932. WinStationTerminate( pWinStation );
  2933. pWinStation->State = State_Down;
  2934. PostErrorValueEvent(EVENT_TS_WINSTATION_START_FAILED, Status);
  2935. WinStationDeleteWorker(pWinStation);
  2936. } else {
  2937. ReleaseWinStation( pWinStation );
  2938. }
  2939. return Status;
  2940. /*
  2941. * Error reading registry data
  2942. */
  2943. badregdata:
  2944. /*
  2945. * WinStation name already exists
  2946. */
  2947. alreadyexists:
  2948. ReleaseWinStation( pWinStation );
  2949. NtClose( pWinStation->Lock.Mutex );
  2950. /*
  2951. * Could not create WinStation lock
  2952. */
  2953. nolock:
  2954. LCDestroyContext(pWinStation);
  2955. /*
  2956. * Could not allocate licensing context
  2957. */
  2958. nolicensecontext:
  2959. MemFree( pWinStation );
  2960. /*
  2961. * Could not allocate WinStation
  2962. */
  2963. nomem:
  2964. PostErrorValueEvent(EVENT_TS_WINSTATION_START_FAILED, Status);
  2965. /*
  2966. * WinStation is disabled
  2967. * System shutdown is in progress
  2968. */
  2969. disabled:
  2970. shutdown:
  2971. return Status;
  2972. }
  2973. /*******************************************************************************
  2974. * WinStationStart
  2975. *
  2976. * Start a WinStation. This involves reading the
  2977. * execute list from the registry, loading the WinStation
  2978. * subsystems, and starting the initial program.
  2979. *
  2980. * ENTRY:
  2981. * pWinStation (input)
  2982. * Pointer to WinStation to start
  2983. ******************************************************************************/
  2984. NTSTATUS WinStationStart(PWINSTATION pWinStation)
  2985. {
  2986. OBJECT_ATTRIBUTES ObjA;
  2987. LARGE_INTEGER Timeout;
  2988. NTSTATUS Status;
  2989. UNICODE_STRING InitialCommand;
  2990. PUNICODE_STRING pInitialCommand;
  2991. PWCHAR pExecuteBuffer = NULL;
  2992. ULONG CommandSize;
  2993. ICA_TRACE Trace;
  2994. PWCHAR pszCsrStartEvent = NULL;
  2995. PWCHAR pszReconEvent = NULL;
  2996. UNICODE_STRING EventName;
  2997. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationStart, %S (LogonId=%d)\n",
  2998. pWinStation->WinStationName, pWinStation->LogonId ));
  2999. // allocate memory
  3000. pExecuteBuffer = MemAlloc( MAX_STRING_BYTES * sizeof(WCHAR) );
  3001. if (pExecuteBuffer == NULL) {
  3002. Status = STATUS_NO_MEMORY;
  3003. goto done;
  3004. }
  3005. pszCsrStartEvent = MemAlloc( MAX_PATH * sizeof(WCHAR) );
  3006. if (pszCsrStartEvent == NULL) {
  3007. Status = STATUS_NO_MEMORY;
  3008. goto done;
  3009. }
  3010. pszReconEvent = MemAlloc( MAX_PATH * sizeof(WCHAR) );
  3011. if (pszReconEvent == NULL) {
  3012. Status = STATUS_NO_MEMORY;
  3013. goto done;
  3014. }
  3015. /*
  3016. * If its a WSF_LISTEN WinStation, see if a specific ACL has
  3017. * been set for it.
  3018. *
  3019. * This ACL will be inherited by WinStations that are
  3020. * connected to from this thread.
  3021. *
  3022. * If not specific ACL has been set, the system default
  3023. * will be used.
  3024. */
  3025. if (( pWinStation->Flags & WSF_LISTEN ) || (pWinStation->LogonId == 0)){
  3026. ReadWinStationSecurityDescriptor( pWinStation );
  3027. }
  3028. /*
  3029. * Open an instance of the TermDD device driver
  3030. */
  3031. Status = IcaOpen( &pWinStation->hIca );
  3032. if ( !NT_SUCCESS( Status ) ) {
  3033. DBGPRINT(( "TERMSRV IcaOpen: Error 0x%x from IcaOpen, last error %d\n",
  3034. Status, GetLastError() ));
  3035. goto done;
  3036. }
  3037. /*
  3038. * Open a stack instance
  3039. */
  3040. Status = IcaStackOpen( pWinStation->hIca, Stack_Primary,
  3041. (PROC)WsxStackIoControl, pWinStation, &pWinStation->hStack);
  3042. if ( !NT_SUCCESS( Status ) ) {
  3043. IcaClose( pWinStation->hIca );
  3044. pWinStation->hIca = NULL;
  3045. DBGPRINT(( "TERMSRV IcaStackOpen: Error 0x%x from IcaStackOpen, last error %d\n",
  3046. Status, GetLastError() ));
  3047. goto done;
  3048. }
  3049. if ( !NT_SUCCESS( Status ) ) {
  3050. IcaClose( pWinStation->hIca );
  3051. pWinStation->hIca = NULL;
  3052. DBGPRINT(( "TERMSRV IcaStackOpen for console stack: Error 0x%x from IcaStackOpen, last error %d\n",
  3053. Status, GetLastError() ));
  3054. }
  3055. /*
  3056. * Enable trace
  3057. */
  3058. RtlZeroMemory( &Trace , sizeof( ICA_TRACE ) );
  3059. InitializeTrace( pWinStation, FALSE, &Trace );
  3060. /*
  3061. * If this is a "listening" WinStation, then we don't load the
  3062. * subsystems and initial command. Instead we create a service
  3063. * thread to wait for new connections and service them.
  3064. */
  3065. if ( pWinStation->Flags & WSF_LISTEN ) {
  3066. DWORD ThreadId;
  3067. pWinStation->hConnectThread = CreateThread(
  3068. NULL,
  3069. 0, // use Default stack size of the svchost process
  3070. (LPTHREAD_START_ROUTINE)WinStationConnectThread,
  3071. LongToPtr( pWinStation->LogonId ),
  3072. 0,
  3073. &ThreadId
  3074. );
  3075. pWinStation->CreateStatus = STATUS_SUCCESS;
  3076. Status = pWinStation->CreateStatus;
  3077. pWinStation->NeverConnected = FALSE;
  3078. pWinStation->State = State_Listen;
  3079. NotifySystemEvent( WEVENT_STATECHANGE );
  3080. /*
  3081. * Load subsystems and initial command
  3082. *
  3083. * Session Manager starts the console itself, but returns the
  3084. * process ID's. For all others, this actually starts CSR
  3085. * and winlogon.
  3086. */
  3087. } else {
  3088. /*
  3089. * Create event we will wait on below
  3090. */
  3091. if ( pWinStation->LogonId ) {
  3092. InitializeObjectAttributes( &ObjA, NULL, 0, NULL, NULL );
  3093. Status = NtCreateEvent( &pWinStation->CreateEvent, EVENT_ALL_ACCESS, &ObjA,
  3094. NotificationEvent, FALSE );
  3095. if ( !NT_SUCCESS( Status ) )
  3096. goto done;
  3097. }
  3098. UnlockWinStation( pWinStation );
  3099. /*
  3100. * Check debugging options
  3101. */
  3102. Status = RegWinStationQueryValueW(
  3103. SERVERNAME_CURRENT,
  3104. pWinStation->WinStationName,
  3105. L"Execute",
  3106. pExecuteBuffer,
  3107. MAX_STRING_BYTES * sizeof(WCHAR),
  3108. &CommandSize );
  3109. if ( !Status && CommandSize ) {
  3110. RtlInitUnicodeString( &InitialCommand, pExecuteBuffer );
  3111. pInitialCommand = &InitialCommand;
  3112. } else {
  3113. pInitialCommand = NULL;
  3114. }
  3115. /*
  3116. * For now only do one winstation start at a time. This is because of
  3117. * WinStation space problems. The Session manager maps it's self into
  3118. * the WinStation space of the CSR it wants to start so the CSR inherits
  3119. * the space. That means only one CSR can be started at a time.
  3120. */
  3121. ENTERCRIT( &WinStationStartCsrLock );
  3122. //Terminal Service needs to skip Memory check in Embedded images
  3123. //when the TS service starts
  3124. //bug #246972
  3125. if(!IsEmbedded()) {
  3126. /*
  3127. * Make sure we have enough resources to start a new session
  3128. */
  3129. Status = NtAllocateVirtualMemory( NtCurrentProcess(),
  3130. &glpAddress,
  3131. 0,
  3132. &gMinPerSessionPageCommit,
  3133. MEM_COMMIT,
  3134. PAGE_READWRITE
  3135. );
  3136. if (!NT_SUCCESS(Status)) {
  3137. DBGPRINT(( "TERMSRV: NtAllocateVirtualMemory failed with Status %lx for Size %lx(MB)\n",Status,gMinPerSessionPageCommitMB));
  3138. LEAVECRIT( &WinStationStartCsrLock );
  3139. goto done;
  3140. } else {
  3141. Status = NtFreeVirtualMemory( NtCurrentProcess(),
  3142. &glpAddress,
  3143. &gMinPerSessionPageCommit,
  3144. MEM_DECOMMIT
  3145. );
  3146. if (!NT_SUCCESS(Status)) {
  3147. DBGPRINT(( "TERMSRV: NtFreeVirtualMemory failed with Status %lx \n",Status));
  3148. ASSERT(NT_SUCCESS(Status));
  3149. }
  3150. }
  3151. }
  3152. Status = SmStartCsr( IcaSmApiPort,
  3153. &pWinStation->LogonId,
  3154. pInitialCommand,
  3155. (PULONG_PTR)&pWinStation->InitialCommandProcessId,
  3156. (PULONG_PTR)&pWinStation->WindowsSubSysProcessId );
  3157. LEAVECRIT( &WinStationStartCsrLock );
  3158. if ( !RelockWinStation( pWinStation ) )
  3159. Status = STATUS_CTX_CLOSE_PENDING;
  3160. if ( Status != STATUS_SUCCESS) {
  3161. DBGPRINT(("TERMSRV: SmStartCsr failed\n"));
  3162. goto done;
  3163. }
  3164. /*
  3165. * Close handle to initial command process, if already opened
  3166. */
  3167. if ( pWinStation->InitialCommandProcess ) {
  3168. NtClose( pWinStation->InitialCommandProcess );
  3169. pWinStation->InitialCommandProcess = NULL;
  3170. }
  3171. /*
  3172. * Open handle to initial command process
  3173. */
  3174. pWinStation->InitialCommandProcess = OpenProcess(
  3175. PROCESS_ALL_ACCESS,
  3176. FALSE,
  3177. (DWORD)(ULONG_PTR)(pWinStation->InitialCommandProcessId) );
  3178. if ( pWinStation->InitialCommandProcess == NULL ) {
  3179. TRACE((hTrace,TC_ICASRV,TT_ERROR,"TERMSRV: Logon %d cannot open Initial command process\n",
  3180. pWinStation->LogonId));
  3181. Status = STATUS_ACCESS_DENIED;
  3182. goto done;
  3183. }
  3184. /*
  3185. * Open handle to WIN32 subsystem process
  3186. */
  3187. pWinStation->WindowsSubSysProcess = OpenProcess(
  3188. PROCESS_ALL_ACCESS,
  3189. FALSE,
  3190. (DWORD)(ULONG_PTR)(pWinStation->WindowsSubSysProcessId) );
  3191. if ( pWinStation->WindowsSubSysProcess == NULL ) {
  3192. TRACE((hTrace,TC_ICASRV,TT_ERROR,"TERMSRV: Logon %d cannot open windows subsystem process\n",
  3193. pWinStation->LogonId));
  3194. Status = STATUS_ACCESS_DENIED;
  3195. goto done;
  3196. }
  3197. //
  3198. // Terminal Server calls into Session Manager to create a new Hydra session.
  3199. // The session manager creates and resume a new session and returns to Terminal
  3200. // server the session id of the new session. There is a race condition where
  3201. // CSR can resume and call into terminal server before terminal server can
  3202. // store the session id in its internal structure. To prevent this CSR will
  3203. // wait here on a named event which will be set by Terminal server once it
  3204. // gets the sessionid for the newly created session
  3205. // Create CsrStartEvent
  3206. //
  3207. if ( NT_SUCCESS( Status ) && pWinStation->LogonId ) {
  3208. wsprintf(pszCsrStartEvent,
  3209. L"\\Sessions\\%d\\BaseNamedObjects\\CsrStartEvent",pWinStation->LogonId);
  3210. RtlInitUnicodeString( &EventName,pszCsrStartEvent);
  3211. InitializeObjectAttributes( &ObjA, &EventName, OBJ_OPENIF, NULL, NULL );
  3212. Status = NtCreateEvent( &(pWinStation->CsrStartEventHandle),
  3213. EVENT_ALL_ACCESS,
  3214. &ObjA,
  3215. NotificationEvent,
  3216. FALSE );
  3217. if ( !NT_SUCCESS( Status ) ) {
  3218. DBGPRINT(("TERMSRV: NtCreateEvent (%ws) failed (%lx)\n",pszCsrStartEvent, Status));
  3219. ASSERT(FALSE);
  3220. pWinStation->CsrStartEventHandle = NULL;
  3221. goto done;
  3222. }
  3223. //
  3224. // Now that we have the sessionId(LogonId), set the CsrStartEvent so
  3225. // CSR can connect to TerminalServer
  3226. //
  3227. NtSetEvent(pWinStation->CsrStartEventHandle, NULL);
  3228. }
  3229. {
  3230. //
  3231. // Create ReconnectReadyEvent
  3232. //
  3233. if ( pWinStation->LogonId == 0 ) {
  3234. wsprintf(pszReconEvent,
  3235. L"\\BaseNamedObjects\\ReconEvent");
  3236. } else {
  3237. wsprintf(pszReconEvent,
  3238. L"\\Sessions\\%d\\BaseNamedObjects\\ReconEvent",pWinStation->LogonId);
  3239. }
  3240. RtlInitUnicodeString( &EventName,pszReconEvent);
  3241. InitializeObjectAttributes( &ObjA, &EventName, OBJ_OPENIF, NULL, NULL );
  3242. Status = NtCreateEvent( &(pWinStation->hReconnectReadyEvent),
  3243. EVENT_ALL_ACCESS,
  3244. &ObjA,
  3245. NotificationEvent,
  3246. TRUE );
  3247. if ( !NT_SUCCESS( Status ) ) {
  3248. DBGPRINT(("TERMSRV: NtCreateEvent (%ws) failed (%lx)\n",pszReconEvent, Status));
  3249. ASSERT(FALSE);
  3250. pWinStation->hReconnectReadyEvent = NULL;
  3251. goto done;
  3252. }
  3253. }
  3254. /*
  3255. * For console, create is always successful - but do we need to
  3256. * crank up the stack for the console session?
  3257. */
  3258. if ( pWinStation->LogonId == 0 )
  3259. {
  3260. pWinStation->CreateStatus = STATUS_SUCCESS;
  3261. Status = pWinStation->CreateStatus;
  3262. pWinStation->NeverConnected = FALSE;
  3263. pWinStation->State = State_Connected;
  3264. /*
  3265. * Wait for create event to be triggered and get create status
  3266. */
  3267. } else {
  3268. Timeout = RtlEnlargedIntegerMultiply( 30000, -10000 );
  3269. UnlockWinStation( pWinStation );
  3270. Status = NtWaitForSingleObject( pWinStation->CreateEvent, FALSE, &Timeout );
  3271. if ( !RelockWinStation( pWinStation ) )
  3272. Status = STATUS_CTX_CLOSE_PENDING;
  3273. if ( Status == STATUS_SUCCESS )
  3274. Status = pWinStation->CreateStatus;
  3275. NtClose( pWinStation->CreateEvent );
  3276. pWinStation->CreateEvent = NULL;
  3277. }
  3278. }
  3279. done:
  3280. if (pExecuteBuffer != NULL) {
  3281. MemFree(pExecuteBuffer);
  3282. pExecuteBuffer = NULL;
  3283. }
  3284. if (pszCsrStartEvent != NULL) {
  3285. MemFree(pszCsrStartEvent);
  3286. pszCsrStartEvent = NULL;
  3287. }
  3288. if (pszReconEvent != NULL) {
  3289. MemFree(pszReconEvent);
  3290. pszReconEvent = NULL;
  3291. }
  3292. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationStart Subsys PID=%d InitialProg PID=%d, Status=0x%x\n",
  3293. pWinStation->WindowsSubSysProcessId,
  3294. pWinStation->InitialCommandProcessId,
  3295. Status ));
  3296. return Status;
  3297. }
  3298. /*******************************************************************************
  3299. * WinStationRenameWorker
  3300. *
  3301. * Worker routine to rename a WinStation.
  3302. *
  3303. * ENTRY:
  3304. * pWinStationNameOld (input)
  3305. * Pointer to old WinStationName
  3306. * pWinStationNameNew (input)
  3307. * Pointer to new WinStationName
  3308. ******************************************************************************/
  3309. NTSTATUS WinStationRenameWorker(
  3310. PWINSTATIONNAME pWinStationNameOld,
  3311. ULONG NameOldSize,
  3312. PWINSTATIONNAME pWinStationNameNew,
  3313. ULONG NameNewSize)
  3314. {
  3315. PWINSTATION pWinStation;
  3316. PLIST_ENTRY Head, Next;
  3317. NTSTATUS Status;
  3318. ULONG ulNewNameLength;
  3319. /*
  3320. * Ensure new WinStation name is non-zero length
  3321. */
  3322. //
  3323. // The new WinStationName is allocated by the RPC server stub and the
  3324. // size is sent over by the client (this is part of the existing interface.
  3325. // Therefore, it is sufficient to assert for the pWinStationNameNew to be
  3326. // non-null AND the New size to be non-zero. If it asserts, this is a serious
  3327. // problem with the rpc stub that should never happen in a released build.
  3328. //
  3329. // The old WinStationName also poses a problem. It is assumed in the code
  3330. // that follows that the old WinStationName is NULL terminated. The RPC
  3331. // interface does not say that. Which means the call to FindWinStation by
  3332. // name can potentially AV.
  3333. if (!( (pWinStationNameNew != 0 ) && (NameNewSize != 0 ) &&
  3334. !IsBadWritePtr( pWinStationNameNew, NameNewSize ) ) ) {
  3335. return( STATUS_CTX_WINSTATION_NAME_INVALID );
  3336. }
  3337. if (!( (pWinStationNameOld != 0 ) && (NameOldSize != 0 )
  3338. && !IsBadReadPtr( pWinStationNameOld, NameOldSize ) &&
  3339. !IsBadWritePtr( pWinStationNameOld, NameOldSize))) {
  3340. return( STATUS_CTX_WINSTATION_NAME_INVALID );
  3341. }
  3342. /*
  3343. * Find and lock the WinStation
  3344. * (Note that we hold the WinStationList lock while changing the name.)
  3345. */
  3346. // We will add a NULL Terminator to the end of the old winstation name
  3347. pWinStationNameOld[ NameOldSize - 1 ] = 0;
  3348. pWinStationNameNew[ NameNewSize - 1 ] = 0;
  3349. /*
  3350. * Ensure new WinStation name is non-zero length and not too long
  3351. */
  3352. ulNewNameLength = wcslen( pWinStationNameNew );
  3353. if ( ( ulNewNameLength == 0 ) || ( ulNewNameLength > WINSTATIONNAME_LENGTH ) )
  3354. return( STATUS_CTX_WINSTATION_NAME_INVALID );
  3355. pWinStation = FindWinStationByName( pWinStationNameOld, TRUE );
  3356. if ( pWinStation == NULL ) {
  3357. LEAVECRIT( &WinStationListLock );
  3358. return( STATUS_CTX_WINSTATION_NOT_FOUND );
  3359. }
  3360. /*
  3361. * Verify that client has DELETE access
  3362. */
  3363. Status = RpcCheckClientAccess( pWinStation, DELETE, FALSE );
  3364. if ( !NT_SUCCESS( Status ) ) {
  3365. LEAVECRIT( &WinStationListLock );
  3366. ReleaseWinStation( pWinStation );
  3367. return( Status );
  3368. }
  3369. /*
  3370. * Now search the WinStation list to see if the new WinStation name
  3371. * is already used. If so then this is an error.
  3372. */
  3373. Head = &WinStationListHead;
  3374. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  3375. PWINSTATION pWinStationTemp;
  3376. pWinStationTemp = CONTAINING_RECORD( Next, WINSTATION, Links );
  3377. if ( !_wcsicmp( pWinStationTemp->WinStationName, pWinStationNameNew ) ) {
  3378. LEAVECRIT( &WinStationListLock );
  3379. ReleaseWinStation( pWinStation );
  3380. return( STATUS_CTX_WINSTATION_NAME_COLLISION );
  3381. }
  3382. }
  3383. /*
  3384. * Free the old name and set the new one, then release
  3385. * the WinStationList lock and the WinStation mutex.
  3386. */
  3387. wcsncpy( pWinStation->WinStationName, pWinStationNameNew, WINSTATIONNAME_LENGTH );
  3388. pWinStation->WinStationName[ WINSTATIONNAME_LENGTH ] = 0;
  3389. LEAVECRIT( &WinStationListLock );
  3390. ReleaseWinStation( pWinStation );
  3391. /*
  3392. * Notify clients of WinStation rename
  3393. */
  3394. NotifySystemEvent( WEVENT_RENAME );
  3395. return STATUS_SUCCESS;
  3396. }
  3397. /*******************************************************************************
  3398. * WinStationTerminate
  3399. *
  3400. * Terminate a WinStation. This involves causing the WinStation initial
  3401. * program to logoff, terminating the initial program, and terminating
  3402. * all subsystems.
  3403. *
  3404. * ENTRY:
  3405. * pWinStation (input)
  3406. * Pointer to WinStation to terminate
  3407. ******************************************************************************/
  3408. VOID WinStationTerminate(PWINSTATION pWinStation)
  3409. {
  3410. WINSTATION_APIMSG msg;
  3411. LARGE_INTEGER Timeout;
  3412. NTSTATUS Status = 0;
  3413. BOOL AllExited = FALSE;
  3414. BOOL bDoDisconnectFailed = FALSE;
  3415. int i, iLoop;
  3416. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationTerminate, %S (LogonId=%d)\n",
  3417. pWinStation->WinStationName, pWinStation->LogonId ));
  3418. //
  3419. // Release filtered address
  3420. //
  3421. /*
  3422. if (pWinStation->pRememberedAddress != NULL) {
  3423. Filter_RemoveOutstandingConnection( &pWinStation->pRememberedAddress->addr[0], pWinStation->pRememberedAddress->length );
  3424. MemFree(pWinStation->pRememberedAddress);
  3425. pWinStation->pRememberedAddress = NULL;
  3426. if( (ULONG)InterlockedDecrement( &NumOutStandingConnect ) == MaxOutStandingConnect )
  3427. {
  3428. if (hConnectEvent != NULL)
  3429. {
  3430. SetEvent(hConnectEvent);
  3431. }
  3432. }
  3433. }
  3434. */
  3435. if (pWinStation->fOwnsConsoleTerminal) {
  3436. CopyReconnectInfo(pWinStation, &ConsoleReconnectInfo);
  3437. }
  3438. /*
  3439. * If not already set, mark the WinStation as terminating.
  3440. * This prevents our WinStation Terminate thread from waiting on
  3441. * the initial program or Win32 subsystem processes.
  3442. */
  3443. ENTERCRIT( &WinStationListLock );
  3444. if ( !pWinStation->Terminating ) {
  3445. pWinStation->Terminating = TRUE;
  3446. NtSetEvent( WinStationEvent, NULL );
  3447. }
  3448. if (!(pWinStation->StateFlags & WSF_ST_WINSTATIONTERMINATE)) {
  3449. pWinStation->StateFlags |= WSF_ST_WINSTATIONTERMINATE;
  3450. } else {
  3451. DBGPRINT(("Termsrv: WinstationTerminate: Session %ld has already been terminated \n",pWinStation->LogonId));
  3452. LEAVECRIT( &WinStationListLock );
  3453. return;
  3454. }
  3455. LEAVECRIT( &WinStationListLock );
  3456. /*
  3457. * If WinStation is idle waiting for a connection, signal connect event
  3458. * - this will return an error back to winlogon
  3459. */
  3460. if ( pWinStation->ConnectEvent ) {
  3461. NtSetEvent( pWinStation->ConnectEvent, NULL );
  3462. }
  3463. /*
  3464. * Stop any shadowing for this WinStation
  3465. */
  3466. WinStationStopAllShadows( pWinStation );
  3467. /*
  3468. * Tell Win32 to disconnect.
  3469. * This puts up the disconnected desktop among other things.
  3470. */
  3471. if ( ( pWinStation->WinStationName[0] ) &&
  3472. ( !pWinStation->NeverConnected ) &&
  3473. ( !(pWinStation->Flags & WSF_LISTEN) ) &&
  3474. ( !(pWinStation->Flags & WSF_DISCONNECT) ) &&
  3475. ( !(pWinStation->StateFlags & WSF_ST_IN_DISCONNECT )) &&
  3476. ( (pWinStation->StateFlags & WSF_ST_CONNECTED_TO_CSRSS) ) ) {
  3477. msg.ApiNumber = SMWinStationDoDisconnect;
  3478. msg.u.DoDisconnect.ConsoleShadowFlag = FALSE;
  3479. /*
  3480. * Insignia really wants the video driver to be notified before
  3481. * the transport is closed.
  3482. */
  3483. pWinStation->StateFlags |= WSF_ST_IN_DISCONNECT;
  3484. Status = SendWinStationCommand( pWinStation, &msg, 600 );
  3485. if (!NT_SUCCESS(Status)) {
  3486. bDoDisconnectFailed = TRUE;
  3487. }else {
  3488. /*
  3489. * Tell csrss to notify winlogon for the disconnect.
  3490. */
  3491. msg.ApiNumber = SMWinStationNotify;
  3492. msg.WaitForReply = FALSE;
  3493. msg.u.DoNotify.NotifyEvent = WinStation_Notify_Disconnect;
  3494. Status = SendWinStationCommand( pWinStation, &msg, 0 );
  3495. pWinStation->StateFlags &= ~WSF_ST_CONNECTED_TO_CSRSS;
  3496. }
  3497. }
  3498. /*
  3499. * Free Timers
  3500. */
  3501. if ( pWinStation->fIdleTimer ) {
  3502. IcaTimerClose( pWinStation->hIdleTimer );
  3503. pWinStation->fIdleTimer = FALSE;
  3504. }
  3505. if ( pWinStation->fLogonTimer ) {
  3506. IcaTimerClose( pWinStation->hLogonTimer );
  3507. pWinStation->fLogonTimer = FALSE;
  3508. }
  3509. if ( pWinStation->fDisconnectTimer ) {
  3510. IcaTimerClose( pWinStation->hDisconnectTimer );
  3511. pWinStation->fDisconnectTimer = FALSE;
  3512. }
  3513. /*
  3514. * Free events
  3515. */
  3516. if ((pWinStation->LogonId == 0 || g_bPersonalTS) && pWinStation->hWinmmConsoleAudioEvent) {
  3517. CloseHandle(pWinStation->hWinmmConsoleAudioEvent);
  3518. }
  3519. /*
  3520. * Notify clients of WinStation delete
  3521. *
  3522. * This mimics what happened in 1.6, but the state of the winstation hasn't changed
  3523. * yet and it's still in the list, so it's not "deleted". Maybe we should add
  3524. * a State_Exiting. Right now, it's marked down when it loses the LPC connection
  3525. * with the CSR. Later, it's removed from the list and another WEVENT_DELETE is sent.
  3526. */
  3527. NotifySystemEvent( WEVENT_DELETE );
  3528. if (!(pWinStation->Flags & WSF_LISTEN))
  3529. {
  3530. UnlockWinStation(pWinStation);
  3531. RemoveSessionNotification( pWinStation->LogonId, pWinStation->SessionSerialNumber );
  3532. /*
  3533. * WinStationDeleteWorker, deletes the lock, which is always called after WinStationTerminate.
  3534. * therefore we should always succeed in Relock here.
  3535. */
  3536. RTL_VERIFY(RelockWinStation(pWinStation));
  3537. }
  3538. /*
  3539. * Terminate ICA stack
  3540. */
  3541. if ( pWinStation->hStack && (!bDoDisconnectFailed) ) {
  3542. /*
  3543. * Close the connection endpoint, if any
  3544. */
  3545. if ( pWinStation->pEndpoint ) {
  3546. /*
  3547. * First notify Wsx that connection is going away
  3548. */
  3549. WsxBrokenConnection( pWinStation );
  3550. IcaStackConnectionClose( pWinStation->hStack,
  3551. &pWinStation->Config,
  3552. pWinStation->pEndpoint,
  3553. pWinStation->EndpointLength
  3554. );
  3555. MemFree( pWinStation->pEndpoint );
  3556. pWinStation->pEndpoint = NULL;
  3557. pWinStation->EndpointLength = 0;
  3558. }
  3559. IcaStackTerminate( pWinStation->hStack );
  3560. } else{
  3561. pWinStation->StateFlags |= WSF_ST_DELAYED_STACK_TERMINATE;
  3562. }
  3563. /*
  3564. * Flush the Win32 command queue.
  3565. * If the Win32 command list is not empty, then loop through each
  3566. * entry on the list and unlink it and trigger the wait event.
  3567. */
  3568. while ( !IsListEmpty( &pWinStation->Win32CommandHead ) ) {
  3569. PLIST_ENTRY Head;
  3570. PCOMMAND_ENTRY pCommand;
  3571. Head = pWinStation->Win32CommandHead.Flink;
  3572. pCommand = CONTAINING_RECORD( Head, COMMAND_ENTRY, Links );
  3573. RemoveEntryList( &pCommand->Links );
  3574. if ( !pCommand->pMsg->WaitForReply ) {
  3575. ASSERT( pCommand->Event == NULL );
  3576. MemFree( pCommand );
  3577. } else {
  3578. pCommand->Links.Flink = NULL;
  3579. pCommand->pMsg->ReturnedStatus = STATUS_CTX_WINSTATION_BUSY;
  3580. NtSetEvent( pCommand->Event, NULL );
  3581. }
  3582. }
  3583. //
  3584. // close CsrStartEvent
  3585. //
  3586. if (pWinStation->CsrStartEventHandle != NULL) {
  3587. NtClose(pWinStation->CsrStartEventHandle);
  3588. }
  3589. //
  3590. // close hReconnectReadyEvent
  3591. //
  3592. if (pWinStation->hReconnectReadyEvent != NULL) {
  3593. NtClose(pWinStation->hReconnectReadyEvent);
  3594. }
  3595. /*
  3596. * Force initial program to exit if it hasn't already
  3597. */
  3598. if ( pWinStation->InitialCommandProcess ) {
  3599. DWORD WaitStatus;
  3600. /*
  3601. * If initial program has already exited, then we can skip this
  3602. */
  3603. WaitStatus = WaitForSingleObject( pWinStation->InitialCommandProcess, 0 );
  3604. if ( WaitStatus != 0 ) {
  3605. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: Terminating initial command, LogonId=%d\n",
  3606. pWinStation->LogonId ));
  3607. //
  3608. // if we are asked to terminate winstation that initiated the shutdown
  3609. // there is no point in sending SMWinStationExitWindows to this window, as its
  3610. // winlogons main thread is already busy waiting for this (RpcWinStationShutdownSystem) lpc
  3611. //.
  3612. if (!ShutDownFromSessionID || ShutDownFromSessionID != pWinStation->LogonId)
  3613. {
  3614. /*
  3615. * Tell the WinStation to logoff
  3616. */
  3617. msg.ApiNumber = SMWinStationExitWindows;
  3618. msg.u.ExitWindows.Flags = EWX_LOGOFF | EWX_FORCE;
  3619. Status = SendWinStationCommand( pWinStation, &msg, 10 );
  3620. if ( NT_SUCCESS( Status ) && ( pWinStation->InitialCommandProcess != NULL ) ) {
  3621. ULONG i;
  3622. if ( ShutDownFromSessionID )
  3623. Timeout = RtlEnlargedIntegerMultiply( 1, -10000 );
  3624. else
  3625. Timeout = RtlEnlargedIntegerMultiply( 2000, -10000 );
  3626. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: Waiting for InitialCommand (ID=0x%x) to exit\n", pWinStation->InitialCommandProcessId ));
  3627. for ( i = 0; i < gLogoffTimeout; i++ ) {
  3628. HANDLE CommandHandle = pWinStation->InitialCommandProcess;
  3629. UnlockWinStation( pWinStation );
  3630. Status = NtWaitForSingleObject( CommandHandle, FALSE, &Timeout );
  3631. RelockWinStation( pWinStation );
  3632. if ( Status == STATUS_SUCCESS )
  3633. break;
  3634. TRACE((hTrace,TC_ICASRV,TT_API1, "." ));
  3635. }
  3636. TRACE((hTrace,TC_ICASRV,TT_API1, "\nTERMSRV: Wait for InitialCommand to exit, Status=0x%x\n", Status ));
  3637. if (Status == STATUS_SUCCESS) {
  3638. NtClose( pWinStation->InitialCommandProcess );
  3639. pWinStation->InitialCommandProcess = NULL;
  3640. }
  3641. }
  3642. }
  3643. else
  3644. {
  3645. // we are not going to have to terminate winlogon for the session that initiated shutdown.
  3646. Status = STATUS_UNSUCCESSFUL;
  3647. }
  3648. /*
  3649. * If unable to connect to the WinStation, then we must use
  3650. * the brute force method - just terminate the initial command.
  3651. */
  3652. if ( ( Status != STATUS_SUCCESS ) && ( pWinStation->InitialCommandProcess != NULL ) ) {
  3653. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: Waiting for InitialCommand to terminate\n" ));
  3654. Status = TerminateProcessAndWait( pWinStation->InitialCommandProcessId,
  3655. pWinStation->InitialCommandProcess,
  3656. 120 );
  3657. if ( Status != STATUS_SUCCESS ) {
  3658. DBGPRINT(( "TERMSRV: InitialCommand failed to terminate, Status=%x\n", Status ));
  3659. /*
  3660. * We can fail terminating initial process if it is waiting
  3661. * for a user validation in the Hard Error popup. In this case it is
  3662. * Ok to proceceed as sending SMWinStationTerminate message bellow
  3663. * will trigger Win32k cleanup code that will dismiss the popup.
  3664. */
  3665. ASSERT(pWinStation->WindowsSubSysProcess);
  3666. }
  3667. }
  3668. }
  3669. NtClose( pWinStation->InitialCommandProcess );
  3670. pWinStation->InitialCommandProcess = NULL;
  3671. }
  3672. /*
  3673. * Now check to see if there are any remaining processes in
  3674. * the system other than CSRSS with this SessionId. If so, terminate them now.
  3675. */
  3676. if (ShutDownFromSessionID && ShutDownFromSessionID == pWinStation->LogonId)
  3677. iLoop = 90;
  3678. else
  3679. iLoop = 3;
  3680. for (i = 0 ; i < iLoop; i++) {
  3681. ULONG NumTerminated = 0;
  3682. AllExited = WinStationTerminateProcesses( pWinStation, &NumTerminated );
  3683. /*
  3684. * If we found any processes other than CSRSS that had to be terminated, we
  3685. * have to re-enumerate all the process and make sure that no new processes
  3686. * in this session were created in the windows between the call to NtQuerySystemInformation
  3687. * and terminating all the found processes. If we only find CSRSS we don't have to
  3688. * re-enumerate since CSRSS does not create any processes.
  3689. */
  3690. if (AllExited && (NumTerminated == 0)) {
  3691. break;
  3692. }
  3693. if ((i == (iLoop - 1)) && (AllExited == FALSE)) {
  3694. /*
  3695. * Last iteration and not all processes terminated
  3696. */
  3697. // DbgBreakPoint();
  3698. }
  3699. /*
  3700. * This is a hack to give enough time to processess to terminate
  3701. */
  3702. if (ShutDownFromSessionID && ShutDownFromSessionID == pWinStation->LogonId)
  3703. {
  3704. Sleep(1*1000);
  3705. }
  3706. else
  3707. {
  3708. Sleep(30*1000);
  3709. }
  3710. }
  3711. if (pWinStation->WindowsSubSysProcess) {
  3712. /*
  3713. * Send terminate message to this subsystem
  3714. */
  3715. msg.ApiNumber = SMWinStationTerminate;
  3716. /*
  3717. * We used to not wait for this. However, if the reverse LPC is
  3718. * hung, the CSR is not going to exit anyway and we don't want
  3719. * to silently forget about the WinStation. (It takes up memory.)
  3720. *
  3721. * Also, if we kill the thread prematurely W32 is never going to exit.
  3722. */
  3723. Status = SendWinStationCommand( pWinStation, &msg, -1 );
  3724. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: Call to SMWinStationTerminate returned Status=0x%x\n", Status));
  3725. if ((Status != STATUS_SUCCESS) && (Status != STATUS_CTX_CLOSE_PENDING) ) {
  3726. SetRefLockDeleteProc(&pWinStation->Lock, WinStationZombieProc);
  3727. }
  3728. /*
  3729. * Now check to see if there are any remaining processes in
  3730. * the system other than CSRSS with this SessionId. If so, terminate them now.
  3731. */
  3732. for (i = 0 ; i < 3; i++) {
  3733. ULONG NumTerminated = 0;
  3734. AllExited = WinStationTerminateProcesses( pWinStation, &NumTerminated );
  3735. /*
  3736. * If we found any processes other than CSRSS that had to be terminated, we
  3737. * have to re-enumerate all the process and make sure that no new processes
  3738. * in this session were created in the windows between the call to NtQuerySystemInformation
  3739. * and terminating all the found processes. If we only find CSRSS we don't have to
  3740. * re-enumerate since CSRSS does not create any processes.
  3741. */
  3742. if (AllExited && (NumTerminated == 0)) {
  3743. break;
  3744. }
  3745. if ((i == 2) && (AllExited == FALSE)) {
  3746. /*
  3747. * Last iteration and not all processes terminated
  3748. */
  3749. // DbgBreakPoint();
  3750. }
  3751. /*
  3752. * This is a hack to give enough time to processess to terminate
  3753. */
  3754. Sleep(30*1000);
  3755. }
  3756. /*
  3757. * Force the windows subsystem to exit. Only terminate CSRSS it all other processes
  3758. * have terminated
  3759. */
  3760. if ( AllExited ) {
  3761. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: All process exited in Session %d\n",pWinStation->LogonId ));
  3762. /*
  3763. * Wait for the subsystem to exit
  3764. */
  3765. if ( NT_SUCCESS(Status) || ( Status == STATUS_CTX_WINSTATION_BUSY ) || (Status == STATUS_CTX_CLOSE_PENDING ) ) {
  3766. ASSERT(!(pWinStation->Flags & WSF_LISTEN));
  3767. // ENTERCRIT( &WinStationStartCsrLock );
  3768. // Status = SmStopCsr( IcaSmApiPort,
  3769. // pWinStation->LogonId );
  3770. // LEAVECRIT( &WinStationStartCsrLock );
  3771. // DBGPRINT(( "TERMSRV: SmStopCsr on CSRSS for Session=%d returned Status=%x\n",
  3772. // pWinStation->LogonId, Status ));
  3773. //
  3774. // ASSERT(NT_SUCCESS(Status));
  3775. // if (!NT_SUCCESS(Status)) {
  3776. // DBGPRINT(( "TERMSRV: SmStopCsr Failed for Session=%d returned Status=%x\n",
  3777. // pWinStation->LogonId, Status ));
  3778. // DbgBreakPoint();
  3779. //}
  3780. NtClose( pWinStation->WindowsSubSysProcess );
  3781. pWinStation->WindowsSubSysProcess = NULL;
  3782. }
  3783. } else {
  3784. DBGPRINT(("TERMSRV: Did not terminate all the session processes\n"));
  3785. SetRefLockDeleteProc(&pWinStation->Lock, WinStationZombieProc);
  3786. // DbgBreakPoint();
  3787. }
  3788. }
  3789. }
  3790. /*******************************************************************************
  3791. * WinStationTerminateProcesses
  3792. *
  3793. * Terminate all processes executing on the specified WinStation
  3794. ******************************************************************************/
  3795. BOOL WinStationTerminateProcesses(
  3796. PWINSTATION pWinStation,
  3797. ULONG *pNumTerminated)
  3798. {
  3799. PCHAR pBuffer;
  3800. ULONG ByteCount;
  3801. NTSTATUS Status;
  3802. PSYSTEM_PROCESS_INFORMATION ProcessInfo;
  3803. UNICODE_STRING CsrssName;
  3804. UNICODE_STRING NtsdName;
  3805. BOOL retval = TRUE;
  3806. WCHAR ProcessName[MAX_PATH];
  3807. SYSTEM_SESSION_PROCESS_INFORMATION SessionProcessInfo;
  3808. ULONG retlen = 0;
  3809. ByteCount = 32 * 1024;
  3810. *pNumTerminated = 0;
  3811. SessionProcessInfo.SessionId = pWinStation->LogonId;
  3812. for ( ; ; ) {
  3813. if ( (pBuffer = MemAlloc( ByteCount )) == NULL )
  3814. return (FALSE);
  3815. SessionProcessInfo.Buffer = pBuffer;
  3816. SessionProcessInfo.SizeOfBuf = ByteCount;
  3817. /*
  3818. * get process info
  3819. */
  3820. Status = NtQuerySystemInformation(
  3821. SystemSessionProcessInformation,
  3822. &SessionProcessInfo,
  3823. sizeof(SessionProcessInfo),
  3824. &retlen );
  3825. if ( NT_SUCCESS( Status ) )
  3826. break;
  3827. /*
  3828. * Make sure buffer is big enough
  3829. */
  3830. MemFree( pBuffer );
  3831. if ( Status != STATUS_INFO_LENGTH_MISMATCH )
  3832. return (FALSE);
  3833. ByteCount *= 2;
  3834. }
  3835. if (retlen == 0) {
  3836. MemFree(pBuffer);
  3837. return TRUE;
  3838. }
  3839. RtlInitUnicodeString(&CsrssName,L"CSRSS");
  3840. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)pBuffer;
  3841. for ( ; ; ) {
  3842. HANDLE ProcessHandle;
  3843. CLIENT_ID ClientId;
  3844. OBJECT_ATTRIBUTES ObjA;
  3845. if (RtlPrefixUnicodeString(&CsrssName,&(ProcessInfo->ImageName),TRUE)) {
  3846. if (ProcessInfo->NextEntryOffset == 0)
  3847. break;
  3848. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)
  3849. ((ULONG_PTR)ProcessInfo + ProcessInfo->NextEntryOffset);
  3850. continue;
  3851. }
  3852. RtlInitUnicodeString(&NtsdName,L"ntsd");
  3853. if (! RtlPrefixUnicodeString(&NtsdName,&(ProcessInfo->ImageName),TRUE) ) {
  3854. // If we found any process other than CSRSS and ntsd.exe, bump the
  3855. // the count
  3856. (*pNumTerminated) += 1;
  3857. }
  3858. /*
  3859. * Found a process with a matching LogonId.
  3860. * Attempt to open the process and terminate it.
  3861. */
  3862. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: TerminateProcesses, found processid 0x%x for LogonId %d\n",
  3863. ProcessInfo->UniqueProcessId, ProcessInfo->SessionId ));
  3864. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: Process Name %ws for LogonId %d\n",
  3865. ProcessInfo->ImageName.Buffer, ProcessInfo->SessionId ));
  3866. ClientId.UniqueThread = 0;
  3867. ClientId.UniqueProcess = (HANDLE)ProcessInfo->UniqueProcessId;
  3868. InitializeObjectAttributes( &ObjA, NULL, 0, NULL, NULL );
  3869. Status = NtOpenProcess( &ProcessHandle, PROCESS_ALL_ACCESS,
  3870. &ObjA, &ClientId );
  3871. if (!NT_SUCCESS(Status)) {
  3872. DBGPRINT(("TERMSRV: Unable to open processid 0x%x, status=0x%x\n",
  3873. ProcessInfo->UniqueProcessId, Status ));
  3874. retval = FALSE;
  3875. } else {
  3876. Status = TerminateProcessAndWait( ProcessInfo->UniqueProcessId,
  3877. ProcessHandle, 60 );
  3878. NtClose( ProcessHandle );
  3879. if ( Status != STATUS_SUCCESS ) {
  3880. DBGPRINT(("TERMSRV: Unable to terminate processid 0x%x, status=0x%x\n",
  3881. ProcessInfo->UniqueProcessId, Status ));
  3882. retval = FALSE;
  3883. }
  3884. }
  3885. if ( ProcessInfo->NextEntryOffset == 0 )
  3886. break;
  3887. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)
  3888. ((ULONG_PTR)ProcessInfo + ProcessInfo->NextEntryOffset);
  3889. }
  3890. /*
  3891. * free buffer
  3892. */
  3893. MemFree( pBuffer );
  3894. return retval;
  3895. }
  3896. /*******************************************************************************
  3897. * WinStationDeleteWorker
  3898. *
  3899. * Delete a WinStation.
  3900. ******************************************************************************/
  3901. VOID WinStationDeleteWorker(PWINSTATION pWinStation)
  3902. {
  3903. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationDeleteWorker, %S (LogonId=%d)\n",
  3904. pWinStation->WinStationName, pWinStation->LogonId ));
  3905. /*
  3906. * If this is the last reference, then
  3907. * Initial program and all subsystems should be terminated by now.
  3908. */
  3909. ENTERCRIT( &WinStationListLock );
  3910. ASSERT( (pWinStation->Links.Flink != NULL) && (pWinStation->Links.Blink != NULL));
  3911. RemoveEntryList( &pWinStation->Links );
  3912. #if DBG
  3913. pWinStation->Links.Flink = pWinStation->Links.Blink = NULL;
  3914. #endif
  3915. if (pWinStation->Lock.RefCount == 1) {
  3916. ASSERT( pWinStation->InitialCommandProcess == NULL );
  3917. }
  3918. // Keep track of total session count for Load Balancing Indicator but don't
  3919. // track listen winstations
  3920. if (!(pWinStation->Flags & WSF_LISTEN))
  3921. WinStationTotalCount--;
  3922. // If we're resetting a disconnected session then adjust LB counter
  3923. if (pWinStation->State == State_Disconnected) {
  3924. WinStationDiscCount--;
  3925. }
  3926. LEAVECRIT( &WinStationListLock );
  3927. /*
  3928. * Unlock WinStation and delete it
  3929. */
  3930. DeleteRefLock( &pWinStation->Lock );
  3931. /*
  3932. * Notify clients of deletion
  3933. */
  3934. NotifySystemEvent( WEVENT_DELETE );
  3935. }
  3936. /*******************************************************************************
  3937. * WinStationDeleteProc
  3938. *
  3939. * Delete the WinStation containing the specified RefLock.
  3940. *
  3941. * ENTRY:
  3942. * pLock (input)
  3943. * Pointer to RefLock of WinStation to delete
  3944. ******************************************************************************/
  3945. VOID WinStationDeleteProc(PREFLOCK pLock)
  3946. {
  3947. PWINSTATION pWinStation;
  3948. ICA_TRACE IcaTrace;
  3949. NTSTATUS Status = STATUS_SUCCESS;
  3950. /*
  3951. * See if we need to wakeup IdleControlThread to maintain Console session
  3952. */
  3953. if ((USER_SHARED_DATA->ActiveConsoleId == -1) && (gConsoleCreationDisable == 0) ) {
  3954. NtSetEvent(WinStationIdleControlEvent, NULL);
  3955. }
  3956. /*
  3957. * Get a pointer to the containing WinStation
  3958. */
  3959. pWinStation = CONTAINING_RECORD( pLock, WINSTATION, Lock );
  3960. /*
  3961. * Release filtered address
  3962. */
  3963. if (pWinStation->pRememberedAddress != NULL) {
  3964. Filter_RemoveOutstandingConnection( &pWinStation->pRememberedAddress->addr[0], pWinStation->pRememberedAddress->length );
  3965. MemFree(pWinStation->pRememberedAddress);
  3966. pWinStation->pRememberedAddress = NULL;
  3967. if( (ULONG)InterlockedDecrement( &NumOutStandingConnect ) == MaxOutStandingConnect )
  3968. {
  3969. if (hConnectEvent != NULL)
  3970. {
  3971. SetEvent(hConnectEvent);
  3972. }
  3973. }
  3974. }
  3975. /*
  3976. * If this hasn't yet been cleaned up do it now.
  3977. */
  3978. if (pWinStation->ConnectEvent) {
  3979. NtClose( pWinStation->ConnectEvent );
  3980. pWinStation->ConnectEvent = NULL;
  3981. }
  3982. if (pWinStation->CreateEvent) {
  3983. NtClose( pWinStation->CreateEvent );
  3984. pWinStation->CreateEvent = NULL;
  3985. }
  3986. /*
  3987. * In the case where we timed out disconnecting the session we had
  3988. * to delay the stack unload till here to avoid situation where Win32k
  3989. * Display driver believe the session is still connected while the WD
  3990. * is already unloaded.
  3991. */
  3992. if ( pWinStation->hStack && (pWinStation->StateFlags & WSF_ST_DELAYED_STACK_TERMINATE) ) {
  3993. pWinStation->StateFlags &= ~WSF_ST_DELAYED_STACK_TERMINATE;
  3994. /*
  3995. * Close the connection endpoint, if any
  3996. */
  3997. if ( pWinStation->pEndpoint ) {
  3998. /*
  3999. * First notify Wsx that connection is going away
  4000. */
  4001. WsxBrokenConnection( pWinStation );
  4002. IcaStackConnectionClose( pWinStation->hStack,
  4003. &pWinStation->Config,
  4004. pWinStation->pEndpoint,
  4005. pWinStation->EndpointLength
  4006. );
  4007. MemFree( pWinStation->pEndpoint );
  4008. pWinStation->pEndpoint = NULL;
  4009. pWinStation->EndpointLength = 0;
  4010. }
  4011. IcaStackTerminate( pWinStation->hStack );
  4012. }
  4013. /* close cdm */
  4014. if ( pWinStation->pWsx &&
  4015. pWinStation->pWsx->pWsxCdmDisconnect ) {
  4016. pWinStation->pWsx->pWsxCdmDisconnect( pWinStation->pWsxContext,
  4017. pWinStation->LogonId,
  4018. pWinStation->hIca );
  4019. }
  4020. /*
  4021. * Call WinStation rundown function before killing the stack
  4022. */
  4023. if ( pWinStation->pWsxContext ) {
  4024. if ( pWinStation->pWsx &&
  4025. pWinStation->pWsx->pWsxWinStationRundown ) {
  4026. pWinStation->pWsx->pWsxWinStationRundown( pWinStation->pWsxContext );
  4027. }
  4028. pWinStation->pWsxContext = NULL;
  4029. }
  4030. /*
  4031. * Close ICA stack and device handles
  4032. */
  4033. if ( pWinStation->hStack ) {
  4034. IcaStackClose( pWinStation->hStack );
  4035. pWinStation->hStack = NULL;
  4036. }
  4037. if ( pWinStation->hIca ) {
  4038. /* close trace */
  4039. memset( &IcaTrace, 0, sizeof(IcaTrace) );
  4040. (void) IcaIoControl( pWinStation->hIca, IOCTL_ICA_SET_TRACE,
  4041. &IcaTrace, sizeof(IcaTrace), NULL, 0, NULL );
  4042. /* close handle */
  4043. IcaClose( pWinStation->hIca );
  4044. pWinStation->hIca = NULL;
  4045. }
  4046. /*
  4047. * Close various ICA channel handles
  4048. */
  4049. if ( pWinStation->hIcaBeepChannel ) {
  4050. (void) IcaChannelClose( pWinStation->hIcaBeepChannel );
  4051. pWinStation->hIcaBeepChannel = NULL;
  4052. }
  4053. if ( pWinStation->hIcaThinwireChannel ) {
  4054. (void) IcaChannelClose( pWinStation->hIcaThinwireChannel );
  4055. pWinStation->hIcaThinwireChannel = NULL;
  4056. }
  4057. if ( pWinStation->hConnectThread ) {
  4058. NtClose( pWinStation->hConnectThread );
  4059. pWinStation->hConnectThread = NULL;
  4060. }
  4061. /*
  4062. * Free security structures
  4063. */
  4064. WinStationFreeSecurityDescriptor( pWinStation );
  4065. if ( pWinStation->pUserSid ) {
  4066. pWinStation->pProfileSid = pWinStation->pUserSid;
  4067. pWinStation->pUserSid = NULL;
  4068. }
  4069. if (pWinStation->pProfileSid) {
  4070. WinstationUnloadProfile(pWinStation);
  4071. MemFree( pWinStation->pProfileSid );
  4072. pWinStation->pProfileSid = NULL;
  4073. }
  4074. /*
  4075. * Cleanup UserToken
  4076. */
  4077. if ( pWinStation->UserToken ) {
  4078. NtClose( pWinStation->UserToken );
  4079. pWinStation->UserToken = NULL;
  4080. }
  4081. if (pWinStation->LogonId > 0) {
  4082. ENTERCRIT( &WinStationStartCsrLock );
  4083. Status = SmStopCsr( IcaSmApiPort, pWinStation->LogonId );
  4084. LEAVECRIT( &WinStationStartCsrLock );
  4085. }
  4086. // Clean up the New Client Credentials struct for Long UserName
  4087. if (pWinStation->pNewClientCredentials != NULL) {
  4088. MemFree(pWinStation->pNewClientCredentials);
  4089. }
  4090. // Clean up the updated Notification Credentials
  4091. if (pWinStation->pNewNotificationCredentials != NULL) {
  4092. MemFree(pWinStation->pNewNotificationCredentials);
  4093. pWinStation->pNewNotificationCredentials = NULL;
  4094. }
  4095. /*
  4096. * Cleanup licensing context
  4097. */
  4098. LCDestroyContext(pWinStation);
  4099. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: SmStopCsr on CSRSS for Session=%d returned Status=%x\n", pWinStation->LogonId, Status ));
  4100. ASSERT(NT_SUCCESS(Status));
  4101. if (!NT_SUCCESS(Status)) {
  4102. DBGPRINT(( "TERMSRV: SmStopCsr Failed for Session=%d returned Status=%x\n", pWinStation->LogonId, Status ));
  4103. // DbgBreakPoint();
  4104. ENTERCRIT( &WinStationZombieLock );
  4105. InsertTailList( &ZombieListHead, &pWinStation->Links );
  4106. LEAVECRIT( &WinStationZombieLock );
  4107. return;
  4108. }
  4109. /*
  4110. * Zero WinStation name buffer
  4111. */
  4112. RtlZeroMemory( pWinStation->WinStationName, sizeof(pWinStation->WinStationName) );
  4113. MemFree( pWinStation );
  4114. }
  4115. /*******************************************************************************
  4116. * WinStationZombieProc
  4117. *
  4118. * Puts WinStation containing the specified RefLock in the zombie list.
  4119. *
  4120. * ENTRY:
  4121. * pLock (input)
  4122. * Pointer to RefLock of WinStation to delete
  4123. ******************************************************************************/
  4124. VOID WinStationZombieProc(PREFLOCK pLock)
  4125. {
  4126. PWINSTATION pWinStation;
  4127. pWinStation = CONTAINING_RECORD( pLock, WINSTATION, Lock );
  4128. ENTERCRIT( &WinStationZombieLock );
  4129. InsertTailList( &ZombieListHead, &pWinStation->Links );
  4130. LEAVECRIT( &WinStationZombieLock );
  4131. }
  4132. /*******************************************************************************
  4133. * CopyReconnectInfo
  4134. *
  4135. *
  4136. * ENTRY:
  4137. ******************************************************************************/
  4138. BOOL CopyReconnectInfo(PWINSTATION pWinStation, PRECONNECT_INFO pReconnectInfo)
  4139. {
  4140. NTSTATUS Status;
  4141. RtlZeroMemory( pReconnectInfo, sizeof(*pReconnectInfo) );
  4142. /*
  4143. * Save WinStation name and configuration data.
  4144. */
  4145. RtlCopyMemory( pReconnectInfo->WinStationName,
  4146. pWinStation->WinStationName,
  4147. sizeof(WINSTATIONNAME) );
  4148. RtlCopyMemory( pReconnectInfo->ListenName,
  4149. pWinStation->ListenName,
  4150. sizeof(WINSTATIONNAME) );
  4151. RtlCopyMemory( pReconnectInfo->ProtocolName,
  4152. pWinStation->ProtocolName,
  4153. sizeof(pWinStation->ProtocolName) );
  4154. RtlCopyMemory( pReconnectInfo->DisplayDriverName,
  4155. pWinStation->DisplayDriverName,
  4156. sizeof(pWinStation->DisplayDriverName) );
  4157. pReconnectInfo->Config = pWinStation->Config;
  4158. pReconnectInfo->Client = pWinStation->Client;
  4159. /*
  4160. * Open a new TS connection to temporarily attach the stack to.
  4161. */
  4162. Status = IcaOpen( &pReconnectInfo->hIca );
  4163. if (Status != STATUS_SUCCESS ) {
  4164. return FALSE;
  4165. }
  4166. Status = IcaStackDisconnect( pWinStation->hStack,
  4167. pReconnectInfo->hIca,
  4168. NULL );
  4169. if ( !NT_SUCCESS( Status ) ){
  4170. IcaClose( pReconnectInfo->hIca );
  4171. pReconnectInfo->hIca = NULL;
  4172. return FALSE;
  4173. }
  4174. /*
  4175. * Save stack and endpoint data
  4176. */
  4177. pReconnectInfo->hStack = pWinStation->hStack;
  4178. pReconnectInfo->pEndpoint = pWinStation->pEndpoint;
  4179. pReconnectInfo->EndpointLength = pWinStation->EndpointLength;
  4180. /*
  4181. * Indicate no stack or connection endpoint for this WinStation
  4182. */
  4183. pWinStation->hStack = NULL;
  4184. pWinStation->pEndpoint = NULL;
  4185. pWinStation->EndpointLength = 0;
  4186. /*
  4187. * Reopen a stack for this WinStation
  4188. */
  4189. Status = IcaStackOpen( pWinStation->hIca, Stack_Primary,
  4190. (PROC)WsxStackIoControl, pWinStation, &pWinStation->hStack );
  4191. /*
  4192. * Save the licensing stuff to move to other winstation
  4193. */
  4194. if ( pWinStation->pWsx &&
  4195. pWinStation->pWsx->pWsxDuplicateContext ) {
  4196. pReconnectInfo->pWsx = pWinStation->pWsx;
  4197. pWinStation->pWsx->pWsxDuplicateContext( pWinStation->pWsxContext,
  4198. &pReconnectInfo->pWsxContext );
  4199. }
  4200. /*
  4201. * Copy console owner info
  4202. */
  4203. pReconnectInfo->fOwnsConsoleTerminal = pWinStation->fOwnsConsoleTerminal;
  4204. /*
  4205. * Copy the notification Credentials to move to other winstation
  4206. */
  4207. if (pWinStation->pNewNotificationCredentials) {
  4208. pReconnectInfo->pNotificationCredentials = pWinStation->pNewNotificationCredentials;
  4209. } else {
  4210. pReconnectInfo->pNotificationCredentials = NULL;
  4211. }
  4212. return TRUE;
  4213. }
  4214. VOID
  4215. _IncrementPnpEvent( VOID )
  4216. {
  4217. HANDLE hPnpMutex = NULL;
  4218. HANDLE hPnpEvent = NULL;
  4219. HANDLE hPnpInfo = NULL;
  4220. typedef struct
  4221. {
  4222. DWORD cbSize;
  4223. LONG cPnpEvents;
  4224. } *PMMPNPINFO;
  4225. PMMPNPINFO pPnpInfo;
  4226. //
  4227. // bump the cPnpEvents
  4228. //
  4229. hPnpMutex = OpenMutex( SYNCHRONIZE, FALSE, L"Global\\GuardMutexmmGlobalPnpInfoGuard" );
  4230. if ( hPnpMutex )
  4231. {
  4232. hPnpEvent = OpenEvent(SYNCHRONIZE, FALSE, L"Global\\GuardEventmmGlobalPnpInfoGuard" );
  4233. if ( hPnpEvent )
  4234. {
  4235. //
  4236. // acquire exclusive ownership on the PnP section
  4237. //
  4238. HANDLE ah[2];
  4239. DWORD dw;
  4240. ah[0] = hPnpEvent;
  4241. ah[1] = hPnpMutex;
  4242. dw = WaitForMultipleObjects( 2, ah, TRUE, 5000 );
  4243. if ( WAIT_TIMEOUT != dw )
  4244. {
  4245. hPnpInfo = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, L"Global\\mmGlobalPnpInfo" );
  4246. if (hPnpInfo) {
  4247. pPnpInfo = (PMMPNPINFO)MapViewOfFile(hPnpInfo, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
  4248. if (pPnpInfo) {
  4249. pPnpInfo->cPnpEvents ++;
  4250. UnmapViewOfFile( pPnpInfo );
  4251. } else {
  4252. DBGPRINT(( "Can't map PnP section: %d\n", GetLastError()));
  4253. }
  4254. CloseHandle(hPnpInfo);
  4255. } else {
  4256. DBGPRINT(( "Can't open PnP section: %d\n", GetLastError()));
  4257. }
  4258. ReleaseMutex( hPnpMutex );
  4259. } else {
  4260. DBGPRINT(( "Timed out to open mmGlobalPnpInfo" ));
  4261. }
  4262. CloseHandle( hPnpEvent );
  4263. } else {
  4264. DBGPRINT(( "Can't open PnP event: %d\n", GetLastError()));
  4265. }
  4266. CloseHandle( hPnpMutex );
  4267. } else {
  4268. DBGPRINT(( "Can't open PnP mutex: %d\n", GetLastError()));
  4269. }
  4270. }
  4271. /*******************************************************************************
  4272. * WinStationDoDisconnect
  4273. *
  4274. * Send disconnect message to a WinStation and optionally close connection
  4275. *
  4276. * ENTRY:
  4277. * pWinStation (input)
  4278. * Pointer to WinStation to disconnect
  4279. * pReconnectInfo (input) OPTIONAL
  4280. * Pointer to RECONNECT_INFO buffer
  4281. * If NULL, this is a terminate disconnect.
  4282. *
  4283. * EXIT:
  4284. * STATUS_SUCCESS - if successful
  4285. * STATUS_CTX_WINSTATION_BUSY - if session is already disconnected, or busy
  4286. ******************************************************************************/
  4287. NTSTATUS WinStationDoDisconnect(
  4288. PWINSTATION pWinStation,
  4289. PRECONNECT_INFO pReconnectInfo,
  4290. BOOLEAN bSyncNotify)
  4291. {
  4292. WINSTATION_APIMSG DisconnectMsg;
  4293. NTSTATUS Status;
  4294. ULONG ulTimeout;
  4295. BOOLEAN fOwnsConsoleTerminal = pWinStation->fOwnsConsoleTerminal;
  4296. FILETIME DiscTime;
  4297. DWORD SessionID;
  4298. BOOLEAN bInformSessionDirectory = FALSE;
  4299. TS_AUTORECONNECTINFO SCAutoReconnectInfo;
  4300. ULONG BytesGot;
  4301. // We need to prevent from WinStationDoDisconnect being called twice
  4302. if ( pWinStation->State == State_Disconnected || pWinStation->StateFlags & WSF_ST_IN_DISCONNECT)
  4303. {
  4304. // The session is already disconnected.
  4305. // BUBUG a specific error code STATUS_CTX_SESSION_DICONNECTED would be better.
  4306. return (STATUS_CTX_WINSTATION_BUSY);
  4307. }
  4308. pWinStation->StateFlags |= WSF_ST_IN_DISCONNECT;
  4309. /*
  4310. * Start disconnect timer if enabled
  4311. */
  4312. if ( ulTimeout = pWinStation->Config.Config.User.MaxDisconnectionTime ) {
  4313. if ( !pWinStation->fDisconnectTimer ) {
  4314. Status = IcaTimerCreate( 0, &pWinStation->hDisconnectTimer );
  4315. if ( NT_SUCCESS( Status ) )
  4316. pWinStation->fDisconnectTimer = TRUE;
  4317. else
  4318. DBGPRINT(("xxxWinStationDisconnect - failed to create timer \n"));
  4319. }
  4320. if ( pWinStation->fDisconnectTimer )
  4321. IcaTimerStart( pWinStation->hDisconnectTimer, DisconnectTimeout,
  4322. LongToPtr( pWinStation->LogonId ), ulTimeout );
  4323. }
  4324. /*
  4325. * Stop any shadowing for this WinStation
  4326. */
  4327. WinStationStopAllShadows( pWinStation );
  4328. /*
  4329. * Tell Win32k about the disconnect
  4330. */
  4331. if (pWinStation->StateFlags & WSF_ST_CONNECTED_TO_CSRSS) {
  4332. DisconnectMsg.ApiNumber = SMWinStationDoDisconnect;
  4333. DisconnectMsg.u.DoDisconnect.ConsoleShadowFlag = FALSE;
  4334. Status = SendWinStationCommand( pWinStation, &DisconnectMsg, 600 );
  4335. if ( !NT_SUCCESS(Status) ) {
  4336. TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: CSR DoDisconnect failed LogonId=%d Status=0x%x\n",
  4337. pWinStation->LogonId, Status ));
  4338. goto badwin32disconnect;
  4339. } else {
  4340. ULONG WaitTime = 0;
  4341. /*
  4342. * Tell csrss to notify winlogon for the disconnect.
  4343. */
  4344. if (pWinStation->UserName[0] != L'\0') {
  4345. DisconnectMsg.WaitForReply = TRUE;
  4346. WaitTime = 10;
  4347. } else {
  4348. DisconnectMsg.WaitForReply = FALSE;
  4349. }
  4350. DisconnectMsg.ApiNumber = SMWinStationNotify;
  4351. if (bSyncNotify) {
  4352. DisconnectMsg.u.DoNotify.NotifyEvent = WinStation_Notify_SyncDisconnect;
  4353. } else {
  4354. DisconnectMsg.u.DoNotify.NotifyEvent = WinStation_Notify_Disconnect;
  4355. }
  4356. Status = SendWinStationCommand( pWinStation, &DisconnectMsg, WaitTime );
  4357. pWinStation->StateFlags &= ~WSF_ST_CONNECTED_TO_CSRSS;
  4358. pWinStation->fOwnsConsoleTerminal = FALSE;
  4359. }
  4360. }
  4361. /*
  4362. * close cdm
  4363. */
  4364. if ( pWinStation->pWsx && pWinStation->pWsx->pWsxCdmDisconnect ) {
  4365. pWinStation->pWsx->pWsxCdmDisconnect( pWinStation->pWsxContext,
  4366. pWinStation->LogonId,
  4367. pWinStation->hIca );
  4368. }
  4369. /*
  4370. * If a reconnect info struct has been specified, then this is NOT
  4371. * a terminate disconnect. Save the current WinStation name,
  4372. * WinStation and client configuration info, and license data.
  4373. * Also disconnect the current stack and save the stack handle
  4374. * and connection endpoint data.
  4375. */
  4376. if ( pReconnectInfo || fOwnsConsoleTerminal) {
  4377. if ((pReconnectInfo == NULL) && fOwnsConsoleTerminal) {
  4378. pReconnectInfo = &ConsoleReconnectInfo;
  4379. if (ConsoleReconnectInfo.hIca) {
  4380. CleanupReconnect(&ConsoleReconnectInfo);
  4381. RtlZeroMemory(&ConsoleReconnectInfo,sizeof(RECONNECT_INFO));
  4382. }
  4383. }
  4384. if (!CopyReconnectInfo(pWinStation, pReconnectInfo))
  4385. {
  4386. Status = STATUS_UNSUCCESSFUL;
  4387. goto badstackopen;
  4388. }
  4389. /*
  4390. * Copy console owner info
  4391. */
  4392. pReconnectInfo->fOwnsConsoleTerminal = fOwnsConsoleTerminal;
  4393. /*
  4394. * This is a terminate disconnect.
  4395. * If there is a connection endpoint, then close it now.
  4396. */
  4397. } else if (pWinStation->pEndpoint ) {
  4398. /*
  4399. * First grab any autoreconnect info state and save it
  4400. * in the winstation
  4401. */
  4402. TRACE((hTrace,TC_ICASRV,TT_API1,
  4403. "TERMSRV: Disconnecting - grabbing SC autoreconnect from stack\n"));
  4404. if (pWinStation->pWsx &&
  4405. pWinStation->pWsx->pWsxEscape) {
  4406. Status = pWinStation->pWsx->pWsxEscape(
  4407. pWinStation->pWsxContext,
  4408. GET_SC_AUTORECONNECT_INFO,
  4409. NULL,
  4410. 0,
  4411. &SCAutoReconnectInfo,
  4412. sizeof(SCAutoReconnectInfo),
  4413. &BytesGot);
  4414. if (NT_SUCCESS(Status)) {
  4415. //
  4416. // Valid the length of the SC info and save it into the winstation
  4417. // this will be used later on. We need to grab the info now
  4418. // before the stack handle is closed as we won't be able to IOCTL
  4419. // down to the stack at autoreconnect time.
  4420. //
  4421. if (SCAutoReconnectInfo.cbAutoReconnectInfo ==
  4422. sizeof(pWinStation->AutoReconnectInfo.ArcRandomBits)) {
  4423. TRACE((hTrace,TC_ICASRV,TT_API1,
  4424. "TERMSRV: Disconnecting - got SC ARC from stack\n"));
  4425. pWinStation->AutoReconnectInfo.Valid = TRUE;
  4426. memcpy(&pWinStation->AutoReconnectInfo.ArcRandomBits,
  4427. &SCAutoReconnectInfo.AutoReconnectInfo,
  4428. sizeof(pWinStation->AutoReconnectInfo.ArcRandomBits));
  4429. }
  4430. else {
  4431. TRACE((hTrace,TC_ICASRV,TT_ERROR,
  4432. "TERMSRV: Disconnecting - got invalid len SC ARC from stack\n"));
  4433. ResetAutoReconnectInfo(pWinStation);
  4434. }
  4435. }
  4436. else {
  4437. TRACE((hTrace,TC_ICASRV,TT_API1,
  4438. "TERMSRV: Disconnecting - did not get SC ARC from stack\n"));
  4439. ResetAutoReconnectInfo(pWinStation);
  4440. }
  4441. }
  4442. /*
  4443. * First notify Wsx that connection is going away
  4444. */
  4445. WsxBrokenConnection( pWinStation );
  4446. if (pWinStation->hStack != NULL) {
  4447. Status = IcaStackConnectionClose( pWinStation->hStack,
  4448. &pWinStation->Config,
  4449. pWinStation->pEndpoint,
  4450. pWinStation->EndpointLength );
  4451. ASSERT( NT_SUCCESS(Status) );
  4452. if ( !NT_SUCCESS(Status) ) {
  4453. TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: StackConnectionClose failed LogonId=%d Status=0x%x\n",
  4454. pWinStation->LogonId, Status ));
  4455. }
  4456. }
  4457. MemFree( pWinStation->pEndpoint );
  4458. pWinStation->pEndpoint = NULL;
  4459. pWinStation->EndpointLength = 0;
  4460. /*
  4461. * Close the stack and reopen it.
  4462. * What we really need is a function to unload the stack drivers
  4463. * but leave the stack handle open.
  4464. */
  4465. if (pWinStation->hStack != NULL) {
  4466. Status = IcaStackClose( pWinStation->hStack );
  4467. ASSERT( NT_SUCCESS( Status ) );
  4468. }
  4469. Status = IcaStackOpen( pWinStation->hIca, Stack_Primary,
  4470. (PROC)WsxStackIoControl, pWinStation, &pWinStation->hStack );
  4471. /*
  4472. * Since this is a terminate disconnect, clear all client
  4473. * license data and indicate it no longer holds a license.
  4474. */
  4475. if ( pWinStation->pWsx &&
  4476. pWinStation->pWsx->pWsxClearContext ) {
  4477. pWinStation->pWsx->pWsxClearContext( pWinStation->pWsxContext );
  4478. }
  4479. /*
  4480. * Session 0, we want to get rid of any protocol extension so that next remote
  4481. * connection could happen with a different protocol.
  4482. */
  4483. if (pWinStation->LogonId == 0 ) {
  4484. if ( pWinStation->pWsxContext ) {
  4485. if ( pWinStation->pWsx &&
  4486. pWinStation->pWsx->pWsxWinStationRundown ) {
  4487. pWinStation->pWsx->pWsxWinStationRundown( pWinStation->pWsxContext );
  4488. }
  4489. pWinStation->pWsxContext = NULL;
  4490. }
  4491. pWinStation->pWsx = NULL;
  4492. }
  4493. }
  4494. /*
  4495. * Cancel timers
  4496. */
  4497. if ( pWinStation->fIdleTimer ) {
  4498. pWinStation->fIdleTimer = FALSE;
  4499. IcaTimerClose( pWinStation->hIdleTimer );
  4500. }
  4501. if ( pWinStation->fLogonTimer ) {
  4502. pWinStation->fLogonTimer = FALSE;
  4503. IcaTimerClose( pWinStation->hLogonTimer );
  4504. }
  4505. // Send Audit Information only for actual disconnects
  4506. if (pWinStation->UserName && (wcslen(pWinStation->UserName) > 0)) {
  4507. AuditEvent( pWinStation, SE_AUDITID_SESSION_DISCONNECTED );
  4508. }
  4509. {
  4510. ENTERCRIT( &WinStationListLock );
  4511. (VOID) NtQuerySystemTime( &pWinStation->DisconnectTime );
  4512. if ((pWinStation->State == State_Active) || (pWinStation->State ==
  4513. State_Shadow)) {
  4514. // If the session was active or in a shadow state and is being
  4515. // disconnected...
  4516. //
  4517. // Copy off the session ID and disconnection FileTime for the
  4518. // session directory call below. We do not want to hold locks when
  4519. // calling the directory interface.
  4520. memcpy(&DiscTime, &pWinStation->DisconnectTime, sizeof(DiscTime));
  4521. SessionID = pWinStation->LogonId;
  4522. // Set flag that we need to notify the session directory.
  4523. bInformSessionDirectory = TRUE;
  4524. }
  4525. pWinStation->State = State_Disconnected;
  4526. RtlZeroMemory( pWinStation->WinStationName,
  4527. sizeof(pWinStation->WinStationName) );
  4528. RtlZeroMemory( pWinStation->ListenName,
  4529. sizeof(pWinStation->ListenName) );
  4530. // Keep track of disconnected session count for Load Balancing
  4531. // Indicator.
  4532. WinStationDiscCount++;
  4533. LEAVECRIT( &WinStationListLock );
  4534. NotifySystemEvent( WEVENT_DISCONNECT | WEVENT_STATECHANGE );
  4535. }
  4536. // Call the session directory to inform of the disconnection.
  4537. if (!g_bPersonalTS && g_fAppCompat && bInformSessionDirectory)
  4538. SessDirNotifyDisconnection(SessionID, DiscTime);
  4539. TRACE((hTrace, TC_ICASRV, TT_API1,
  4540. "TERMSRV: WinStationDoDisconnect, rc=0x0\n" ));
  4541. Status = NotifyDisconnect(pWinStation, fOwnsConsoleTerminal);
  4542. if ( !NT_SUCCESS(Status) ) {
  4543. DBGPRINT(("NotifyConsoleDisconnect failed, SessionId = %d, Status = "
  4544. "%d", pWinStation->LogonId, Status));
  4545. }
  4546. pWinStation->StateFlags &= ~WSF_ST_IN_DISCONNECT;
  4547. return STATUS_SUCCESS;
  4548. /*=============================================================================
  4549. == Error returns
  4550. =============================================================================*/
  4551. badstackopen:
  4552. badwin32disconnect:
  4553. TRACE((hTrace, TC_ICASRV, TT_API1, "TERMSRV: WinStationDoDisconnect, rc=0x%x\n", Status ));
  4554. pWinStation->StateFlags &= ~WSF_ST_IN_DISCONNECT;
  4555. return Status;
  4556. }
  4557. /*******************************************************************************
  4558. * ImperonateClient
  4559. *
  4560. * Giving a client primary, make the calling thread impersonate the client
  4561. *
  4562. * ENTRY:
  4563. * ClientToken (input)
  4564. * A client primary token
  4565. * pImpersonationToken (output)
  4566. * Pointer to an impersonation token
  4567. ******************************************************************************/
  4568. NTSTATUS _ImpersonateClient(HANDLE ClientToken, HANDLE *pImpersonationToken)
  4569. {
  4570. SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
  4571. OBJECT_ATTRIBUTES ObjA;
  4572. NTSTATUS Status;
  4573. //
  4574. // ClientToken is a primary token - create an impersonation token
  4575. // version of it so we can set it on our thread
  4576. //
  4577. InitializeObjectAttributes( &ObjA, NULL, 0L, NULL, NULL );
  4578. SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
  4579. SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
  4580. SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
  4581. SecurityQualityOfService.EffectiveOnly = FALSE;
  4582. ObjA.SecurityQualityOfService = &SecurityQualityOfService;
  4583. Status = NtDuplicateToken( ClientToken,
  4584. TOKEN_IMPERSONATE,
  4585. &ObjA,
  4586. FALSE,
  4587. TokenImpersonation,
  4588. pImpersonationToken );
  4589. if ( !NT_SUCCESS( Status ) )
  4590. {
  4591. TRACE( ( hTrace, TC_ICASRV, TT_ERROR, "ImpersonateClient: cannot get impersonation token: 0x%x\n", Status ) );
  4592. return( Status );
  4593. }
  4594. //
  4595. // Impersonate the client
  4596. //
  4597. Status = NtSetInformationThread( NtCurrentThread(),
  4598. ThreadImpersonationToken,
  4599. ( PVOID )pImpersonationToken,
  4600. ( ULONG )sizeof( HANDLE ) );
  4601. if ( !NT_SUCCESS( Status ) )
  4602. {
  4603. TRACE( ( hTrace, TC_ICASRV, TT_ERROR, "ImpersonateClient: cannot impersonate client: 0x%x\n", Status ) );
  4604. }
  4605. return Status;
  4606. }
  4607. /*******************************************************************************
  4608. * WinStationDoReconnect
  4609. *
  4610. * Send connect Api message to a WinStation.
  4611. *
  4612. * ENTRY:
  4613. * pWinStation (input)
  4614. * Pointer to WinStation to connect
  4615. * pReconnectInfo (input)
  4616. * Pointer to RECONNECT_INFO buffer
  4617. ******************************************************************************/
  4618. NTSTATUS WinStationDoReconnect(
  4619. PWINSTATION pWinStation,
  4620. PRECONNECT_INFO pReconnectInfo)
  4621. {
  4622. WINSTATION_APIMSG ReconnectMsg;
  4623. NTSTATUS Status;
  4624. BOOLEAN fDisableCdm;
  4625. BOOLEAN fDisableCpm;
  4626. BOOLEAN fDisableLPT;
  4627. BOOLEAN fDisableCcm;
  4628. BOOLEAN fDisableClip;
  4629. NTSTATUS TempStatus;
  4630. PWINSTATIONCONFIG2 pCurConfig = NULL;
  4631. PWINSTATIONCLIENT pCurClient = NULL;
  4632. // WinStation should not currently be connected
  4633. ASSERT( pWinStation->pEndpoint == NULL );
  4634. //
  4635. // Allocate and initialize CurConfig struct
  4636. //
  4637. if ( (pCurConfig = MemAlloc( sizeof(WINSTATIONCONFIG2) )) == NULL ) {
  4638. Status = STATUS_NO_MEMORY;
  4639. goto nomem;
  4640. }
  4641. RtlZeroMemory( pCurConfig, sizeof(WINSTATIONCONFIG2) );
  4642. //
  4643. // Allocate and initialize CurClient struct
  4644. //
  4645. if ( (pCurClient = MemAlloc( sizeof(WINSTATIONCLIENT) )) == NULL ) {
  4646. Status = STATUS_NO_MEMORY;
  4647. goto nomem;
  4648. }
  4649. RtlZeroMemory( pCurClient, sizeof(WINSTATIONCLIENT) );
  4650. //
  4651. // Config info has to be set prior to calling CSRSS. CSRSS notifies winlogon
  4652. // which in turn sends reconnect messages to notification dlls. We query
  4653. // protocol info from termsrv notification dll which is stored in config
  4654. // data
  4655. //
  4656. *pCurConfig = pWinStation->Config;
  4657. pWinStation->Config = pReconnectInfo->Config;
  4658. *pCurClient = pWinStation->Client;
  4659. pWinStation->Client = pReconnectInfo->Client;
  4660. if ((pWinStation->LogonId == 0) && (pWinStation->UserName[0] == L'\0')) {
  4661. ReconnectMsg.ApiNumber = SMWinStationNotify;
  4662. ReconnectMsg.WaitForReply = TRUE;
  4663. ReconnectMsg.u.DoNotify.NotifyEvent = WinStation_Notify_PreReconnect;
  4664. Status = SendWinStationCommand( pWinStation, &ReconnectMsg, 60 );
  4665. }
  4666. /*
  4667. * Unconditionally, we will send the PreReconnectDesktopSwitch event.
  4668. */
  4669. ReconnectMsg.ApiNumber = SMWinStationNotify;
  4670. ReconnectMsg.WaitForReply = TRUE;
  4671. ReconnectMsg.u.DoNotify.NotifyEvent = WinStation_Notify_PreReconnectDesktopSwitch;
  4672. Status = SendWinStationCommand( pWinStation, &ReconnectMsg, 60 );
  4673. /*
  4674. * Close the current stack and reconnect the saved stack to this WinStation
  4675. */
  4676. if (pWinStation->hStack != NULL) {
  4677. IcaStackClose( pWinStation->hStack );
  4678. pWinStation->hStack = NULL;
  4679. }
  4680. Status = IcaStackReconnect( pReconnectInfo->hStack,
  4681. pWinStation->hIca,
  4682. pWinStation,
  4683. pWinStation->LogonId );
  4684. if ( !NT_SUCCESS( Status ) ){
  4685. pWinStation->Config = *pCurConfig;
  4686. pWinStation->Client = *pCurClient;
  4687. goto badstackreconnect;
  4688. }
  4689. /*
  4690. * Save stack and endpoint data
  4691. */
  4692. pWinStation->hStack = pReconnectInfo->hStack;
  4693. pWinStation->pEndpoint = pReconnectInfo->pEndpoint;
  4694. pWinStation->EndpointLength = pReconnectInfo->EndpointLength;
  4695. /*
  4696. * Save the notification Credentials in the new winstation
  4697. */
  4698. if (pReconnectInfo->pNotificationCredentials) {
  4699. if (pWinStation->pNewNotificationCredentials == NULL) {
  4700. pWinStation->pNewNotificationCredentials = MemAlloc(sizeof(CLIENTNOTIFICATIONCREDENTIALS));
  4701. if (pWinStation->pNewNotificationCredentials == NULL) {
  4702. Status = STATUS_NO_MEMORY ;
  4703. goto nomem ;
  4704. }
  4705. }
  4706. RtlCopyMemory( pWinStation->pNewNotificationCredentials->Domain,
  4707. pReconnectInfo->pNotificationCredentials->Domain,
  4708. sizeof(pReconnectInfo->pNotificationCredentials->Domain) );
  4709. RtlCopyMemory( pWinStation->pNewNotificationCredentials->UserName,
  4710. pReconnectInfo->pNotificationCredentials->UserName,
  4711. sizeof(pReconnectInfo->pNotificationCredentials->UserName) );
  4712. } else {
  4713. pWinStation->pNewNotificationCredentials = NULL;
  4714. }
  4715. pReconnectInfo->hStack = NULL;
  4716. pReconnectInfo->pEndpoint = NULL;
  4717. pReconnectInfo->EndpointLength = 0;
  4718. pReconnectInfo->pNotificationCredentials = NULL;
  4719. /*
  4720. * Tell Win32k about the reconnect
  4721. */
  4722. ReconnectMsg.ApiNumber = SMWinStationDoReconnect;
  4723. ReconnectMsg.u.DoReconnect.fMouse = (BOOLEAN)pReconnectInfo->Client.fMouse;
  4724. ReconnectMsg.u.DoReconnect.fClientDoubleClickSupport =
  4725. (BOOLEAN)pReconnectInfo->Client.fDoubleClickDetect;
  4726. ReconnectMsg.u.DoReconnect.fEnableWindowsKey =
  4727. (BOOLEAN)pReconnectInfo->Client.fEnableWindowsKey;
  4728. RtlCopyMemory( ReconnectMsg.u.DoReconnect.WinStationName,
  4729. pReconnectInfo->WinStationName,
  4730. sizeof(WINSTATIONNAME) );
  4731. RtlCopyMemory( ReconnectMsg.u.DoReconnect.AudioDriverName,
  4732. pReconnectInfo->Client.AudioDriverName,
  4733. sizeof( ReconnectMsg.u.DoReconnect.AudioDriverName ) );
  4734. RtlCopyMemory( ReconnectMsg.u.DoReconnect.DisplayDriverName,
  4735. pReconnectInfo->DisplayDriverName,
  4736. sizeof( ReconnectMsg.u.DoReconnect.DisplayDriverName ) );
  4737. RtlCopyMemory( ReconnectMsg.u.DoReconnect.ProtocolName,
  4738. pReconnectInfo->ProtocolName,
  4739. sizeof( ReconnectMsg.u.DoReconnect.ProtocolName ) );
  4740. /*
  4741. * Set the display resolution information in the message for reconnection
  4742. */
  4743. ReconnectMsg.u.DoReconnect.HRes = pReconnectInfo->Client.HRes;
  4744. ReconnectMsg.u.DoReconnect.VRes = pReconnectInfo->Client.VRes;
  4745. ReconnectMsg.u.DoReconnect.ProtocolType = pReconnectInfo->Client.ProtocolType;
  4746. ReconnectMsg.u.DoReconnect.fDynamicReconnect = (BOOLEAN)(pWinStation->Config.Wd.WdFlag & WDF_DYNAMIC_RECONNECT );
  4747. /*
  4748. * Translate the color to the format excpected in winsrv
  4749. */
  4750. switch (pReconnectInfo->Client.ColorDepth) {
  4751. case 1:
  4752. ReconnectMsg.u.DoReconnect.ColorDepth=4 ; // 16 colors
  4753. break;
  4754. case 2:
  4755. ReconnectMsg.u.DoReconnect.ColorDepth=8 ; // 256
  4756. break;
  4757. case 4:
  4758. ReconnectMsg.u.DoReconnect.ColorDepth= 16;// 64K
  4759. break;
  4760. case 8:
  4761. ReconnectMsg.u.DoReconnect.ColorDepth= 24;// 16M
  4762. break;
  4763. #define DC_HICOLOR
  4764. #ifdef DC_HICOLOR
  4765. case 16:
  4766. ReconnectMsg.u.DoReconnect.ColorDepth= 15;// 32K
  4767. break;
  4768. #endif
  4769. default:
  4770. ReconnectMsg.u.DoReconnect.ColorDepth=8 ;
  4771. break;
  4772. }
  4773. ReconnectMsg.u.DoReconnect.KeyboardType = pWinStation->Client.KeyboardType;
  4774. ReconnectMsg.u.DoReconnect.KeyboardSubType = pWinStation->Client.KeyboardSubType;
  4775. ReconnectMsg.u.DoReconnect.KeyboardFunctionKey = pWinStation->Client.KeyboardFunctionKey;
  4776. if (pWinStation->LogonId == 0 || g_bPersonalTS) {
  4777. if (pWinStation->hWinmmConsoleAudioEvent) {
  4778. if (pWinStation->Client.fRemoteConsoleAudio) {
  4779. // set the remoting audio on console flag
  4780. SetEvent(pWinStation->hWinmmConsoleAudioEvent);
  4781. }
  4782. else {
  4783. // don't set the remoting audio on console flag
  4784. ResetEvent(pWinStation->hWinmmConsoleAudioEvent);
  4785. }
  4786. _IncrementPnpEvent();
  4787. }
  4788. }
  4789. if (WaitForSingleObject(pWinStation->hReconnectReadyEvent, 45*1000) != WAIT_OBJECT_0) {
  4790. DbgPrint("Wait Failed for hReconnectReadyEvent for Session %d\n", pWinStation->LogonId);
  4791. SetEvent(pWinStation->hReconnectReadyEvent);
  4792. }
  4793. Status = SendWinStationCommand( pWinStation, &ReconnectMsg, 600 );
  4794. if ( !NT_SUCCESS(Status) ) {
  4795. TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: CSR DoReconnect failed LogonId=%d Status=0x%x\n",
  4796. pWinStation->LogonId, Status ));
  4797. pWinStation->Config = *pCurConfig;
  4798. pWinStation->Client = *pCurClient;
  4799. goto badreconnect;
  4800. } else {
  4801. pWinStation->StateFlags |= WSF_ST_CONNECTED_TO_CSRSS;
  4802. }
  4803. //
  4804. // Update protocol and display driver names.
  4805. //
  4806. RtlCopyMemory( pWinStation->ProtocolName,
  4807. pReconnectInfo->ProtocolName,
  4808. sizeof(pWinStation->ProtocolName) );
  4809. RtlCopyMemory( pWinStation->DisplayDriverName,
  4810. pReconnectInfo->DisplayDriverName,
  4811. sizeof(pWinStation->DisplayDriverName) );
  4812. //
  4813. // Set session time zone information.
  4814. //
  4815. #ifdef TERMSRV_USE_CLIENT_TIME_ZONE
  4816. {
  4817. WINSTATION_APIMSG TimezoneMsg;
  4818. memset( &TimezoneMsg, 0, sizeof(TimezoneMsg) );
  4819. TimezoneMsg.ApiNumber = SMWinStationSetTimeZone;
  4820. memcpy(&(TimezoneMsg.u.SetTimeZone.TimeZone),&(pReconnectInfo->Client.ClientTimeZone),
  4821. sizeof(TS_TIME_ZONE_INFORMATION));
  4822. SendWinStationCommand( pWinStation, &TimezoneMsg, 600 );
  4823. }
  4824. #endif
  4825. /*
  4826. * Copy console owner info
  4827. */
  4828. pWinStation->fOwnsConsoleTerminal = pReconnectInfo->fOwnsConsoleTerminal;
  4829. /*
  4830. * Close temporary ICA connection that was opened for the reconnect
  4831. */
  4832. IcaClose( pReconnectInfo->hIca );
  4833. pReconnectInfo->hIca = NULL;
  4834. /*
  4835. * Move all of the licensing stuff to the new WinStation
  4836. */
  4837. /*
  4838. * we may not have pWsx if the ReconnectInfo is from a session
  4839. * that was connected to the local console
  4840. */
  4841. if ( pReconnectInfo->pWsxContext ) {
  4842. if ( pWinStation->pWsx == NULL ) {
  4843. //
  4844. // This means that we are reconnecting remotely to a session
  4845. // that comes from the console. So create a new extension.
  4846. //
  4847. pWinStation->pWsx = FindWinStationExtensionDll(
  4848. pWinStation->Config.Wd.WsxDLL,
  4849. pWinStation->Config.Wd.WdFlag );
  4850. //
  4851. // Initialize winstation extension context structure
  4852. //
  4853. if ( pWinStation->pWsx &&
  4854. pWinStation->pWsx->pWsxWinStationInitialize ) {
  4855. Status = pWinStation->pWsx->pWsxWinStationInitialize(
  4856. &pWinStation->pWsxContext);
  4857. if (!NT_SUCCESS(Status)) {
  4858. pWinStation->pWsx = NULL;
  4859. }
  4860. }
  4861. if ( pWinStation->pWsx &&
  4862. pWinStation->pWsx->pWsxWinStationReInitialize ) {
  4863. WSX_INFO WsxInfo;
  4864. WsxInfo.Version = WSX_INFO_VERSION_1;
  4865. WsxInfo.hIca = pWinStation->hIca;
  4866. WsxInfo.hStack = pWinStation->hStack;
  4867. WsxInfo.SessionId = pWinStation->LogonId;
  4868. WsxInfo.pDomain = pWinStation->Domain;
  4869. WsxInfo.pUserName = pWinStation->UserName;
  4870. Status = pWinStation->pWsx->pWsxWinStationReInitialize(
  4871. pWinStation->pWsxContext, &WsxInfo );
  4872. if (!NT_SUCCESS(Status)) {
  4873. pWinStation->pWsx = NULL;
  4874. }
  4875. }
  4876. }
  4877. if ( pWinStation->pWsx &&
  4878. pWinStation->pWsx->pWsxCopyContext ) {
  4879. pWinStation->pWsx->pWsxCopyContext( pWinStation->pWsxContext,
  4880. pReconnectInfo->pWsxContext );
  4881. }
  4882. if ( pReconnectInfo->pWsx &&
  4883. pReconnectInfo->pWsx->pWsxWinStationRundown ) {
  4884. pReconnectInfo->pWsx->pWsxWinStationRundown( pReconnectInfo->pWsxContext );
  4885. }
  4886. pReconnectInfo->pWsxContext = NULL;
  4887. } else { // pReconnectInfo->pWsxContext == NULL
  4888. //
  4889. // This means that we are reconnecting to the console.
  4890. //
  4891. if ( pWinStation->pWsx &&
  4892. pWinStation->pWsx->pWsxWinStationRundown ) {
  4893. //
  4894. // Reconnecting a remote session to the console.
  4895. // Delete the extension.
  4896. //
  4897. pWinStation->pWsx->pWsxWinStationRundown( pWinStation->pWsxContext );
  4898. }
  4899. pWinStation->pWsxContext = NULL;
  4900. pWinStation->pWsx = NULL;
  4901. //
  4902. // In the case where both are NULL, we are reconnecting
  4903. // to the console a session that comes from the console.
  4904. //
  4905. }
  4906. RtlEnterCriticalSection( &WinStationListLock );
  4907. if (pWinStation->UserName[0] != (WCHAR) 0) {
  4908. pWinStation->State = State_Active;
  4909. } else {
  4910. pWinStation->State = State_Connected;
  4911. }
  4912. RtlCopyMemory( pWinStation->WinStationName,
  4913. pReconnectInfo->WinStationName,
  4914. sizeof(WINSTATIONNAME) );
  4915. /*
  4916. * Copy the original listen name for instance checking.
  4917. */
  4918. RtlCopyMemory( pWinStation->ListenName,
  4919. pReconnectInfo->ListenName,
  4920. sizeof(WINSTATIONNAME) );
  4921. // Keep track of disconnected session count for Load Balancing Indicator
  4922. WinStationDiscCount--;
  4923. RtlLeaveCriticalSection( &WinStationListLock );
  4924. /*
  4925. * Disable virtual channel flags are from the transport setup.
  4926. * Do not overwrite them.
  4927. */
  4928. fDisableCdm = (BOOLEAN) pWinStation->Config.Config.User.fDisableCdm;
  4929. fDisableCpm = (BOOLEAN) pWinStation->Config.Config.User.fDisableCpm;
  4930. fDisableLPT = (BOOLEAN) pWinStation->Config.Config.User.fDisableLPT;
  4931. fDisableCcm = (BOOLEAN) pWinStation->Config.Config.User.fDisableCcm;
  4932. fDisableClip = (BOOLEAN) pWinStation->Config.Config.User.fDisableClip;
  4933. pWinStation->Config = pReconnectInfo->Config;
  4934. pWinStation->Config.Config.User.fDisableCdm = fDisableCdm;
  4935. pWinStation->Config.Config.User.fDisableCpm = fDisableCpm;
  4936. pWinStation->Config.Config.User.fDisableLPT = fDisableLPT;
  4937. pWinStation->Config.Config.User.fDisableCcm = fDisableCcm;
  4938. pWinStation->Config.Config.User.fDisableClip = fDisableClip;
  4939. /*
  4940. * Disable virtual channels if needed.
  4941. */
  4942. VirtualChannelSecurity( pWinStation );
  4943. /*
  4944. * Notify the CDM channel of reconnection.
  4945. */
  4946. if ( pWinStation->pWsx &&
  4947. pWinStation->pWsx->pWsxCdmConnect ) {
  4948. (VOID) pWinStation->pWsx->pWsxCdmConnect( pWinStation->pWsxContext,
  4949. pWinStation->LogonId,
  4950. pWinStation->hIca );
  4951. }
  4952. /*
  4953. * Reset any autoreconnect information prior to reconnection
  4954. * as it is stale. New information will be generated by the stack
  4955. * when login completes.
  4956. */
  4957. ResetAutoReconnectInfo(pWinStation);
  4958. if ( pWinStation->pWsx &&
  4959. pWinStation->pWsx->pWsxLogonNotify ) {
  4960. PWCHAR pUserNameToSend, pDomainToSend ;
  4961. // Use the New notification credentials sent from Gina for the call below if they are available
  4962. if (pWinStation->pNewNotificationCredentials) {
  4963. pUserNameToSend = pWinStation->pNewNotificationCredentials->UserName;
  4964. pDomainToSend = pWinStation->pNewNotificationCredentials->Domain;
  4965. } else {
  4966. pUserNameToSend = pWinStation->UserName;
  4967. pDomainToSend = pWinStation->Domain ;
  4968. }
  4969. Status = pWinStation->pWsx->pWsxLogonNotify( pWinStation->pWsxContext,
  4970. pWinStation->LogonId,
  4971. NULL,
  4972. pDomainToSend,
  4973. pUserNameToSend);
  4974. if (pWinStation->pNewNotificationCredentials != NULL) {
  4975. MemFree(pWinStation->pNewNotificationCredentials);
  4976. pWinStation->pNewNotificationCredentials = NULL;
  4977. }
  4978. if(!NT_SUCCESS(Status)) {
  4979. TRACE((hTrace, TC_ICASRV, TT_API1,
  4980. "TERMSRV: WinStationDoReconnect: LogonNotify rc=0x%x\n",
  4981. Status ));
  4982. }
  4983. }
  4984. NotifySystemEvent( WEVENT_CONNECT | WEVENT_STATECHANGE );
  4985. /*
  4986. * Cleanup any allocated buffers.
  4987. * The endpoint buffer was transfered to the WinStation above.
  4988. */
  4989. pReconnectInfo->pEndpoint = NULL;
  4990. pReconnectInfo->EndpointLength = 0;
  4991. /*
  4992. * Set connect time and stop disconnect timer
  4993. */
  4994. NtQuerySystemTime(&pWinStation->ConnectTime);
  4995. if (pWinStation->fDisconnectTimer) {
  4996. pWinStation->fDisconnectTimer = FALSE;
  4997. IcaTimerClose( pWinStation->hDisconnectTimer );
  4998. }
  4999. /*
  5000. * Start logon timers
  5001. */
  5002. StartLogonTimers(pWinStation);
  5003. // Notify the session directory of the reconnection.
  5004. if (!g_bPersonalTS && g_fAppCompat) {
  5005. TSSD_ReconnectSessionInfo ReconnInfo;
  5006. ReconnInfo.SessionID = pWinStation->LogonId;
  5007. ReconnInfo.TSProtocol = pWinStation->Client.ProtocolType;
  5008. ReconnInfo.ResolutionWidth = pWinStation->Client.HRes;
  5009. ReconnInfo.ResolutionHeight = pWinStation->Client.VRes;
  5010. ReconnInfo.ColorDepth = pWinStation->Client.ColorDepth;
  5011. SessDirNotifyReconnection(&ReconnInfo);
  5012. }
  5013. TRACE((hTrace, TC_ICASRV, TT_API1, "TERMSRV: WinStationDoReconnect, rc=0x0\n" ));
  5014. AuditEvent( pWinStation, SE_AUDITID_SESSION_RECONNECTED );
  5015. /*
  5016. * Tell csrss to notify winlogon for the reconnect then notify any process
  5017. * that registred for notification.
  5018. */
  5019. ReconnectMsg.ApiNumber = SMWinStationNotify;
  5020. ReconnectMsg.WaitForReply = FALSE;
  5021. ReconnectMsg.u.DoNotify.NotifyEvent = WinStation_Notify_Reconnect;
  5022. Status = SendWinStationCommand( pWinStation, &ReconnectMsg, 0 );
  5023. Status = NotifyConnect(pWinStation, pWinStation->fOwnsConsoleTerminal);
  5024. if ( !NT_SUCCESS(Status) ) {
  5025. DBGPRINT(("NotifyConsoleConnect failed, SessionId = %d, Status = %d", pWinStation->LogonId, Status));
  5026. }
  5027. // Free up allocated Memory
  5028. if (pCurConfig != NULL) {
  5029. MemFree( pCurConfig );
  5030. pCurConfig = NULL;
  5031. }
  5032. if (pCurClient != NULL) {
  5033. MemFree( pCurClient );
  5034. pCurClient = NULL;
  5035. }
  5036. if (pWinStation->pNewNotificationCredentials != NULL) {
  5037. MemFree(pWinStation->pNewNotificationCredentials);
  5038. pWinStation->pNewNotificationCredentials = NULL;
  5039. }
  5040. // Since the winstation has reconnected, we can allow further autoreconnects
  5041. pWinStation->fDisallowAutoReconnect = FALSE;
  5042. return STATUS_SUCCESS;
  5043. /*=============================================================================
  5044. == Error returns
  5045. =============================================================================*/
  5046. /*
  5047. * Failure from Win32 reconnect call.
  5048. * Disconnect the stack again, and indicate the WinStation
  5049. * does not have a stack or endpoint connection.
  5050. */
  5051. badreconnect:
  5052. TempStatus = IcaStackDisconnect( pWinStation->hStack,
  5053. pReconnectInfo->hIca,
  5054. NULL );
  5055. //ASSERT( NT_SUCCESS( TempStatus ) );
  5056. pReconnectInfo->hStack = pWinStation->hStack;
  5057. pReconnectInfo->pEndpoint = pWinStation->pEndpoint;
  5058. pReconnectInfo->EndpointLength = pWinStation->EndpointLength;
  5059. pWinStation->hStack = NULL;
  5060. pWinStation->pEndpoint = NULL;
  5061. pWinStation->EndpointLength = 0;
  5062. badstackreconnect:
  5063. TempStatus = IcaStackOpen( pWinStation->hIca, Stack_Primary,
  5064. (PROC)WsxStackIoControl, pWinStation, &pWinStation->hStack );
  5065. //ASSERT( NT_SUCCESS( TempStatus ) ); // don't know how to handle any error here
  5066. nomem:
  5067. // Free up allocated Memory
  5068. if (pCurConfig != NULL) {
  5069. MemFree( pCurConfig );
  5070. pCurConfig = NULL;
  5071. }
  5072. if (pCurClient != NULL) {
  5073. MemFree( pCurClient );
  5074. pCurClient = NULL;
  5075. }
  5076. if (pWinStation->pNewNotificationCredentials != NULL) {
  5077. MemFree(pWinStation->pNewNotificationCredentials);
  5078. pWinStation->pNewNotificationCredentials = NULL;
  5079. }
  5080. TRACE((hTrace, TC_ICASRV, TT_API1, "TERMSRV: WinStationDoReconnect, rc=0x%x\n", Status ));
  5081. return( Status );
  5082. }
  5083. /*******************************************************************************
  5084. * WsxBrokenConnection
  5085. *
  5086. * Send broken connection notification to the WinStation extension DLL
  5087. ******************************************************************************/
  5088. VOID WsxBrokenConnection(PWINSTATION pWinStation)
  5089. {
  5090. /*
  5091. * Only send notification if there is a reason
  5092. */
  5093. if ( pWinStation->BrokenReason ) {
  5094. if ( pWinStation->pWsx && pWinStation->pWsx->pWsxBrokenConnection ) {
  5095. ICA_BROKEN_CONNECTION Broken;
  5096. Broken.Reason = pWinStation->BrokenReason;
  5097. Broken.Source = pWinStation->BrokenSource;
  5098. pWinStation->pWsx->pWsxBrokenConnection( pWinStation->pWsxContext,
  5099. pWinStation->hStack,
  5100. &Broken );
  5101. }
  5102. /*
  5103. * Clear these once we have tried to send them
  5104. */
  5105. pWinStation->BrokenReason = 0;
  5106. pWinStation->BrokenSource = 0;
  5107. }
  5108. }
  5109. /*******************************************************************************
  5110. * CleanupReconnect
  5111. *
  5112. * Cleanup the specified RECONNECT_INFO structure
  5113. *
  5114. * ENTRY:
  5115. * pReconnectInfo (input)
  5116. * Pointer to RECONNECT_INFO buffer
  5117. ******************************************************************************/
  5118. VOID CleanupReconnect(PRECONNECT_INFO pReconnectInfo)
  5119. {
  5120. NTSTATUS Status;
  5121. /*
  5122. * If there is a connection endpoint, then close it now.
  5123. * When done, we also free the endpoint structure.
  5124. */
  5125. if ( (pReconnectInfo->pEndpoint != NULL) && (pReconnectInfo->hStack != NULL)) {
  5126. Status = IcaStackConnectionClose( pReconnectInfo->hStack,
  5127. &pReconnectInfo->Config,
  5128. pReconnectInfo->pEndpoint,
  5129. pReconnectInfo->EndpointLength );
  5130. ASSERT( Status == STATUS_SUCCESS );
  5131. MemFree( pReconnectInfo->pEndpoint );
  5132. pReconnectInfo->pEndpoint = NULL;
  5133. }
  5134. if ( pReconnectInfo->pWsxContext ) {
  5135. if ( pReconnectInfo->pWsx &&
  5136. pReconnectInfo->pWsx->pWsxWinStationRundown ) {
  5137. pReconnectInfo->pWsx->pWsxWinStationRundown( pReconnectInfo->pWsxContext );
  5138. }
  5139. pReconnectInfo->pWsxContext = NULL;
  5140. }
  5141. if ( pReconnectInfo->hStack ) {
  5142. IcaStackClose( pReconnectInfo->hStack );
  5143. pReconnectInfo->hStack = NULL;
  5144. }
  5145. if ( pReconnectInfo->hIca ) {
  5146. IcaClose( pReconnectInfo->hIca );
  5147. pReconnectInfo->hIca = NULL;
  5148. }
  5149. }
  5150. NTSTATUS _CloseEndpoint(
  5151. IN PWINSTATIONCONFIG2 pWinStationConfig,
  5152. IN PVOID pEndpoint,
  5153. IN ULONG EndpointLength,
  5154. IN PWINSTATION pWinStation,
  5155. IN BOOLEAN bNeedStack)
  5156. {
  5157. HANDLE hIca;
  5158. HANDLE hStack;
  5159. NTSTATUS Status;
  5160. /*
  5161. * Open a stack handle that we can use to close the specified endpoint.
  5162. */
  5163. TRACE((hTrace, TC_ICASRV, TT_ERROR,
  5164. "TERMSRV: _CloseEndpoint [%p] on %s stack\n",
  5165. pEndpoint, bNeedStack ? "Temporary" : "Primary"));
  5166. if (bNeedStack) {
  5167. Status = IcaOpen( &hIca );
  5168. if ( NT_SUCCESS( Status ) ) {
  5169. Status = IcaStackOpen( hIca, Stack_Primary, NULL, NULL, &hStack );
  5170. if ( NT_SUCCESS( Status ) ) {
  5171. Status = IcaStackConnectionClose( hStack,
  5172. pWinStationConfig,
  5173. pEndpoint,
  5174. EndpointLength );
  5175. IcaStackClose( hStack );
  5176. }
  5177. IcaClose( hIca );
  5178. }
  5179. }
  5180. else {
  5181. Status = IcaStackConnectionClose( pWinStation->hStack,
  5182. pWinStationConfig,
  5183. pEndpoint,
  5184. EndpointLength );
  5185. }
  5186. if ( !NT_SUCCESS( Status ) ) {
  5187. TRACE((hTrace, TC_ICASRV, TT_ERROR,
  5188. "TERMSRV: _CloseEndpoint failed [%s], Status=%x\n",
  5189. bNeedStack ? "Temporary" : "Primary", Status ));
  5190. }
  5191. return Status;
  5192. }
  5193. /*******************************************************************************
  5194. * WinStationExceptionFilter
  5195. *
  5196. * Handle exception from a WinStation thread
  5197. *
  5198. * ENTRY:
  5199. * pExceptionInfo (input)
  5200. * pointer to EXCEPTION_POINTERS struct
  5201. *
  5202. * EXIT:
  5203. * EXCEPTION_EXECUTE_HANDLER -- always
  5204. ******************************************************************************/
  5205. NTSTATUS WinStationExceptionFilter(
  5206. PWSTR OutputString,
  5207. PEXCEPTION_POINTERS pexi)
  5208. {
  5209. PLIST_ENTRY Head, Next;
  5210. PWINSTATION pWinStation;
  5211. MUTANT_BASIC_INFORMATION MutexInfo;
  5212. NTSTATUS Status;
  5213. DbgPrint( "TERMSRV: %S\n", OutputString );
  5214. DbgPrint( "TERMSRV: ExceptionRecord=%p ContextRecord=%p\n",
  5215. pexi->ExceptionRecord, pexi->ContextRecord );
  5216. DbgPrint( "TERMSRV: Exception code=%08x, flags=%08x, addr=%p, IP=%p\n",
  5217. pexi->ExceptionRecord->ExceptionCode,
  5218. pexi->ExceptionRecord->ExceptionFlags,
  5219. pexi->ExceptionRecord->ExceptionAddress,
  5220. CONTEXT_TO_PROGRAM_COUNTER(pexi->ContextRecord) );
  5221. #ifdef i386
  5222. DbgPrint( "TERMSRV: esp=%p ebp=%p\n",
  5223. pexi->ContextRecord->Esp, pexi->ContextRecord->Ebp );
  5224. #endif
  5225. DbgBreakPoint();
  5226. /*
  5227. * Lock the global WinStation critsec if we don't already own it
  5228. */
  5229. if ( NtCurrentTeb()->ClientId.UniqueThread != WinStationListLock.OwningThread )
  5230. ENTERCRIT( &WinStationListLock );
  5231. /*
  5232. * Search the WinStation list to see if we had any locked
  5233. */
  5234. Head = &WinStationListHead;
  5235. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  5236. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  5237. Status = NtQueryMutant( pWinStation->Lock.Mutex, MutantBasicInformation,
  5238. &MutexInfo, sizeof(MutexInfo), NULL );
  5239. if ( NT_SUCCESS( Status ) && MutexInfo.OwnedByCaller ) {
  5240. ReleaseWinStation( pWinStation );
  5241. break; // OK to quit now, we should never lock more than one
  5242. }
  5243. }
  5244. LEAVECRIT( &WinStationListLock );
  5245. return EXCEPTION_EXECUTE_HANDLER;
  5246. }
  5247. /*******************************************************************************
  5248. * GetProcessLogonId
  5249. *
  5250. * Get LogonId for a process
  5251. *
  5252. * ENTRY:
  5253. * ProcessHandle (input)
  5254. * handle of process to get LogonId for
  5255. * pLogonId (output)
  5256. * location to return LogonId of process
  5257. ******************************************************************************/
  5258. NTSTATUS GetProcessLogonId(HANDLE Process, PULONG pLogonId)
  5259. {
  5260. NTSTATUS Status;
  5261. PROCESS_SESSION_INFORMATION ProcessInfo;
  5262. /*
  5263. * Get the LogonId for the process
  5264. */
  5265. *pLogonId = 0;
  5266. Status = NtQueryInformationProcess( Process, ProcessSessionInformation,
  5267. &ProcessInfo, sizeof( ProcessInfo ),
  5268. NULL );
  5269. if ( !NT_SUCCESS( Status ) ) {
  5270. DBGPRINT(( "TERMSRV: GetProcessLogonId, Process=%x, Status=%x\n",
  5271. Process, Status ));
  5272. return( Status );
  5273. }
  5274. *pLogonId = ProcessInfo.SessionId;
  5275. return Status;
  5276. }
  5277. /*******************************************************************************
  5278. * SetProcessLogonId
  5279. *
  5280. * Set LogonId for a process
  5281. *
  5282. * ENTRY:
  5283. * ProcessHandle (input)
  5284. * handle of process to set LogonId for
  5285. * LogonId (output)
  5286. * LogonId to set for process
  5287. ******************************************************************************/
  5288. NTSTATUS SetProcessLogonId(HANDLE Process, ULONG LogonId)
  5289. {
  5290. NTSTATUS Status;
  5291. PROCESS_SESSION_INFORMATION ProcessInfo;
  5292. /*
  5293. * Set the LogonId for the process
  5294. */
  5295. ProcessInfo.SessionId = LogonId;
  5296. Status = NtSetInformationProcess( Process, ProcessSessionInformation,
  5297. &ProcessInfo, sizeof( ProcessInfo ) );
  5298. if ( !NT_SUCCESS( Status ) ) {
  5299. DBGPRINT(( "TERMSRV: SetProcessLogonId, Process=%x, Status=%x\n",
  5300. Process, Status ));
  5301. return Status;
  5302. }
  5303. return Status;
  5304. }
  5305. /*******************************************************************************
  5306. * FindWinStationById
  5307. *
  5308. * Find and lock a WinStation given its LogonId
  5309. *
  5310. * ENTRY:
  5311. * LogonId (input)
  5312. * LogonId of WinStation to find
  5313. * LockList (input)
  5314. * BOOLEAN indicating whether WinStationListLock should be
  5315. * left locked on return
  5316. *
  5317. * EXIT:
  5318. * On success - Pointer to WinStation
  5319. * On failure - NULL
  5320. ******************************************************************************/
  5321. PWINSTATION FindWinStationById(ULONG LogonId, BOOLEAN LockList)
  5322. {
  5323. PLIST_ENTRY Head, Next;
  5324. PWINSTATION pWinStation;
  5325. PWINSTATION pFoundWinStation = NULL;
  5326. ULONG uCount;
  5327. Head = &WinStationListHead;
  5328. ENTERCRIT( &WinStationListLock );
  5329. /*
  5330. * Search the list for a WinStation with the given logonid.
  5331. */
  5332. searchagain:
  5333. uCount = 0;
  5334. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  5335. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  5336. if ( pWinStation->LogonId == LogonId ) {
  5337. uCount++;
  5338. /*
  5339. * Now try to lock the WinStation.
  5340. */
  5341. if (pFoundWinStation == NULL){
  5342. if ( !LockRefLock( &pWinStation->Lock ) )
  5343. goto searchagain;
  5344. pFoundWinStation = pWinStation;
  5345. }
  5346. #if DBG
  5347. #else
  5348. break;
  5349. #endif
  5350. }
  5351. }
  5352. ASSERT((uCount <= 1) || (LogonId== -1) );
  5353. /*
  5354. * If the WinStationList lock should not be held, then release it now.
  5355. */
  5356. if ( !LockList )
  5357. LEAVECRIT( &WinStationListLock );
  5358. if (pFoundWinStation == NULL) {
  5359. TRACE((hTrace,TC_ICASRV,TT_API2,"TERMSRV: FindWinStationById: %d (not found)\n", LogonId ));
  5360. }
  5361. return pFoundWinStation;
  5362. }
  5363. BOOL
  5364. FindFirstListeningWinStationName( PWINSTATIONNAMEW pListenName, PWINSTATIONCONFIG2 pConfig )
  5365. {
  5366. PLIST_ENTRY Head, Next;
  5367. PWINSTATION pWinStation;
  5368. BOOL bFound = FALSE;
  5369. Head = &WinStationListHead;
  5370. ENTERCRIT( &WinStationListLock );
  5371. searchagain:
  5372. /*
  5373. * Search the list for a WinStation with the given name.
  5374. */
  5375. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  5376. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  5377. if ( pWinStation->Flags & WSF_LISTEN && pWinStation->Client.ProtocolType == PROTOCOL_RDP) {
  5378. // try to lock winstation.
  5379. if ( !LockRefLock( &pWinStation->Lock ) )
  5380. goto searchagain;
  5381. CopyMemory( pConfig, &(pWinStation->Config), sizeof(WINSTATIONCONFIG2) );
  5382. lstrcpy( pListenName, pWinStation->WinStationName );
  5383. ReleaseWinStation( pWinStation );
  5384. bFound = TRUE;
  5385. }
  5386. }
  5387. LEAVECRIT( &WinStationListLock );
  5388. TRACE((hTrace,TC_ICASRV,TT_API3,"TERMSRV: FindFirstListeningWinStationName: %ws\n",
  5389. (bFound) ? pListenName : L"Not Found" ));
  5390. return bFound;
  5391. }
  5392. /*******************************************************************************
  5393. * FindWinStationByName
  5394. *
  5395. * Find and lock a WinStation given its Name
  5396. *
  5397. * ENTRY:
  5398. * WinStationName (input)
  5399. * Name of WinStation to find
  5400. * LockList (input)
  5401. * BOOLEAN indicating whether WinStationListLock should be
  5402. * left locked on return
  5403. *
  5404. * EXIT:
  5405. * On success - Pointer to WinStation
  5406. * On failure - NULL
  5407. ******************************************************************************/
  5408. PWINSTATION FindWinStationByName(LPWSTR WinStationName, BOOLEAN LockList)
  5409. {
  5410. PLIST_ENTRY Head, Next;
  5411. PWINSTATION pWinStation;
  5412. Head = &WinStationListHead;
  5413. ENTERCRIT( &WinStationListLock );
  5414. /*
  5415. * Search the list for a WinStation with the given name.
  5416. */
  5417. searchagain:
  5418. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  5419. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  5420. if ( !_wcsicmp( pWinStation->WinStationName, WinStationName ) ) {
  5421. /*
  5422. * Now try to lock the WinStation. If this succeeds,
  5423. * then ensure it still has the name we're searching for.
  5424. */
  5425. if ( !LockRefLock( &pWinStation->Lock ) )
  5426. goto searchagain;
  5427. if ( _wcsicmp( pWinStation->WinStationName, WinStationName ) ) {
  5428. ReleaseWinStation( pWinStation );
  5429. goto searchagain;
  5430. }
  5431. /*
  5432. * If the WinStationList lock should not be held, then release it now.
  5433. */
  5434. if ( !LockList )
  5435. LEAVECRIT( &WinStationListLock );
  5436. TRACE((hTrace,TC_ICASRV,TT_API3,"TERMSRV: FindWinStationByName: %S, LogonId %u\n",
  5437. WinStationName, pWinStation->LogonId ));
  5438. return( pWinStation );
  5439. }
  5440. }
  5441. /*
  5442. * If the WinStationList lock should not be held, then release it now.
  5443. */
  5444. if ( !LockList )
  5445. LEAVECRIT( &WinStationListLock );
  5446. TRACE((hTrace,TC_ICASRV,TT_API3,"TERMSRV: FindWinStationByName: %S, (not found)\n",
  5447. WinStationName ));
  5448. return NULL;
  5449. }
  5450. /*******************************************************************************
  5451. * FindIdleWinStation
  5452. *
  5453. * Find and lock an idle WinStation
  5454. *
  5455. * EXIT:
  5456. * On success - Pointer to WinStation
  5457. * On failure - NULL
  5458. ******************************************************************************/
  5459. PWINSTATION FindIdleWinStation()
  5460. {
  5461. PLIST_ENTRY Head, Next;
  5462. PWINSTATION pWinStation;
  5463. BOOLEAN bFirstTime = TRUE;
  5464. Head = &WinStationListHead;
  5465. ENTERCRIT( &WinStationListLock );
  5466. /*
  5467. * Search the list for an idle WinStation
  5468. */
  5469. searchagain:
  5470. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  5471. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  5472. if ( (pWinStation->Flags & WSF_IDLE) &&
  5473. !(pWinStation->Flags & WSF_IDLEBUSY) &&
  5474. !pWinStation->Starting &&
  5475. pWinStation->ConnectEvent ) {
  5476. /*
  5477. * Now try to lock the WinStation. If this succeeds,
  5478. * then ensure it is still marked as idle.
  5479. */
  5480. if ( !LockRefLock( &pWinStation->Lock ) )
  5481. goto searchagain;
  5482. if ( !(pWinStation->Flags & WSF_IDLE) ||
  5483. (pWinStation->Flags & WSF_IDLEBUSY) ||
  5484. pWinStation->Starting ||
  5485. !pWinStation->ConnectEvent ) {
  5486. ReleaseWinStation( pWinStation );
  5487. goto searchagain;
  5488. }
  5489. LEAVECRIT( &WinStationListLock );
  5490. return( pWinStation );
  5491. }
  5492. }
  5493. LEAVECRIT( &WinStationListLock );
  5494. TRACE((hTrace,TC_ICASRV,TT_API2,"TERMSRV: FindIdleWinStation: (none found)\n" ));
  5495. return NULL;
  5496. }
  5497. /*******************************************************************************
  5498. * CountWinStationType
  5499. *
  5500. * Count the number of matching Winstation Listen Names
  5501. *
  5502. * ENTRY:
  5503. * Listen Name
  5504. *
  5505. * bActiveOnly if TRUE, count only active WinStations
  5506. *
  5507. * EXIT:
  5508. * Number
  5509. ******************************************************************************/
  5510. ULONG CountWinStationType(
  5511. PWINSTATIONNAME pListenName,
  5512. BOOLEAN bActiveOnly,
  5513. BOOLEAN bLockHeld)
  5514. {
  5515. PLIST_ENTRY Head, Next;
  5516. PWINSTATION pWinStation;
  5517. ULONG Count = 0;
  5518. Head = &WinStationListHead;
  5519. if ( !bLockHeld ) {
  5520. ENTERCRIT( &WinStationListLock );
  5521. }
  5522. /*
  5523. * Search the list for an idle WinStation
  5524. */
  5525. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  5526. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  5527. if ( !wcscmp( pWinStation->ListenName, pListenName ) ) {
  5528. if ( !bActiveOnly )
  5529. Count++;
  5530. else if ( pWinStation->State == State_Active )
  5531. Count++;
  5532. }
  5533. }
  5534. if ( !bLockHeld ) {
  5535. LEAVECRIT( &WinStationListLock );
  5536. }
  5537. TRACE((hTrace,TC_ICASRV,TT_API2,"TERMSRV: CountWinstationType %d\n", Count ));
  5538. return Count;
  5539. }
  5540. /*******************************************************************************
  5541. * LockWinStationByPointer
  5542. *
  5543. * Lock a WinStation given a pointer
  5544. *
  5545. * NOTE:
  5546. * WinStationListLock must be locked on entry and will be locked on return.
  5547. * If return value is FALSE, then the WinStation may have beed deleted
  5548. * and the pWinStation pointer should NOT be referenced.
  5549. *
  5550. * ENTRY:
  5551. * pWinStation (input)
  5552. * Pointer to WinStation to lock
  5553. *
  5554. * EXIT:
  5555. * On success - TRUE if WinStation was locked successfully
  5556. * On failure - FALSE otherwise
  5557. ******************************************************************************/
  5558. BOOLEAN LockWinStationByPointer(PWINSTATION pWinStation)
  5559. {
  5560. /*
  5561. * Try to lock the WinStation.
  5562. */
  5563. return LockRefLock(&pWinStation->Lock);
  5564. }
  5565. /*******************************************************************************
  5566. * InitRefLock
  5567. *
  5568. * Initialize a RefLock and lock it.
  5569. *
  5570. * ENTRY:
  5571. * pLock (input)
  5572. * Pointer to RefLock to init
  5573. * pDeleteProcedure (input)
  5574. * Pointer to delete procedure for object
  5575. ******************************************************************************/
  5576. NTSTATUS InitRefLock(PREFLOCK pLock, PREFLOCKDELETEPROCEDURE pDeleteProcedure)
  5577. {
  5578. NTSTATUS Status;
  5579. // Create and lock winstation mutex
  5580. Status = NtCreateMutant( &pLock->Mutex, MUTANT_ALL_ACCESS, NULL, TRUE );
  5581. if ( !NT_SUCCESS( Status ) )
  5582. return( Status );
  5583. pLock->RefCount = 1;
  5584. pLock->Invalid = FALSE;
  5585. pLock->pDeleteProcedure = pDeleteProcedure;
  5586. return STATUS_SUCCESS;
  5587. }
  5588. /*******************************************************************************
  5589. * SetRefLockDeleteProc
  5590. *
  5591. * Cahnge a RefLock DeleteProc.
  5592. *
  5593. * ENTRY:
  5594. * pLock (input)
  5595. * Pointer to RefLock to init
  5596. * pDeleteProcedure (input)
  5597. * Pointer to delete procedure for object
  5598. ******************************************************************************/
  5599. NTSTATUS SetRefLockDeleteProc(
  5600. PREFLOCK pLock,
  5601. PREFLOCKDELETEPROCEDURE pDeleteProcedure)
  5602. {
  5603. pLock->pDeleteProcedure = pDeleteProcedure;
  5604. return STATUS_SUCCESS;
  5605. }
  5606. /*******************************************************************************
  5607. * LockRefLock
  5608. *
  5609. * Increment the reference count for a RefLock and lock it.
  5610. *
  5611. * NOTE:
  5612. * WinStationListLock must be locked on entry and will be locked on return.
  5613. *
  5614. * ENTRY:
  5615. * pLock (input)
  5616. * Pointer to RefLock to lock
  5617. *
  5618. * EXIT:
  5619. * TRUE - if object was locked successfully
  5620. * FALSE - otherwise
  5621. ******************************************************************************/
  5622. BOOLEAN LockRefLock(PREFLOCK pLock)
  5623. {
  5624. /*
  5625. * Increment reference count for this RefLock.
  5626. */
  5627. InterlockedIncrement( &pLock->RefCount );
  5628. /*
  5629. * If mutex cannot be locked without blocking,
  5630. * then unlock the WinStation list lock, wait for the mutex,
  5631. * and relock the WinStation list lock.
  5632. */
  5633. if ( NtWaitForSingleObject( pLock->Mutex, FALSE, &TimeoutZero ) != STATUS_SUCCESS ) {
  5634. LEAVECRIT( &WinStationListLock );
  5635. NtWaitForSingleObject( pLock->Mutex, FALSE, NULL );
  5636. ENTERCRIT( &WinStationListLock );
  5637. /*
  5638. * If the object is marked as invalid, it was removed while
  5639. * we waited for the lock. Release our lock and return FALSE,
  5640. * indicating we were unable to lock it.
  5641. */
  5642. if ( pLock->Invalid ) {
  5643. /*
  5644. * Release the winstationlist lock because the Winstation
  5645. * migth go away as a result of releasing its lock,
  5646. */
  5647. LEAVECRIT( &WinStationListLock );
  5648. ReleaseRefLock( pLock );
  5649. ENTERCRIT( &WinStationListLock );
  5650. return FALSE;
  5651. }
  5652. }
  5653. return TRUE;
  5654. }
  5655. /*******************************************************************************
  5656. * RelockRefLock
  5657. *
  5658. * Relock a RefLock which has been unlocked but still has a reference.
  5659. *
  5660. * NOTE:
  5661. * Object must have been previously unlocked by calling UnlockRefLock.
  5662. *
  5663. * EXIT:
  5664. * TRUE - if object is still valid
  5665. * FALSE - if object was marked invalid while unlocked
  5666. ******************************************************************************/
  5667. BOOLEAN RelockRefLock(PREFLOCK pLock)
  5668. {
  5669. /*
  5670. * Lock the mutex
  5671. */
  5672. NtWaitForSingleObject( pLock->Mutex, FALSE, NULL );
  5673. /*
  5674. * If the object is marked as invalid,
  5675. * it was removed while it was unlocked so we return FALSE.
  5676. */
  5677. return !pLock->Invalid;
  5678. }
  5679. /*******************************************************************************
  5680. * UnlockRefLock
  5681. *
  5682. * Unlock a RefLock but keep a reference to it (don't decrement
  5683. * the reference count). Caller must use RelockWinRefLock
  5684. * to relock the object.
  5685. *
  5686. * ENTRY:
  5687. * pLock (input)
  5688. * Pointer to RefLock to unlock
  5689. ******************************************************************************/
  5690. VOID UnlockRefLock(PREFLOCK pLock)
  5691. {
  5692. NtReleaseMutant(pLock->Mutex, NULL);
  5693. }
  5694. /*******************************************************************************
  5695. * ReleaseRefLock
  5696. *
  5697. * Unlock and dereference a RefLock.
  5698. *
  5699. * ENTRY:
  5700. * pLock (input)
  5701. * Pointer to RefLock to release
  5702. ******************************************************************************/
  5703. VOID ReleaseRefLock(PREFLOCK pLock)
  5704. {
  5705. ASSERT( pLock->RefCount > 0 );
  5706. /*
  5707. * If object has been marked invalid and we are the
  5708. * last reference, then finish deleting it now.
  5709. */
  5710. if ( pLock->Invalid ) {
  5711. ULONG RefCount;
  5712. RefCount = InterlockedDecrement( &pLock->RefCount );
  5713. NtReleaseMutant( pLock->Mutex, NULL );
  5714. if ( RefCount == 0 ) {
  5715. NtClose( pLock->Mutex );
  5716. (*pLock->pDeleteProcedure)( pLock );
  5717. }
  5718. } else {
  5719. InterlockedDecrement( &pLock->RefCount );
  5720. NtReleaseMutant( pLock->Mutex, NULL );
  5721. }
  5722. }
  5723. /*******************************************************************************
  5724. * DeleteRefLock
  5725. *
  5726. * Unlock, dereference, and delete a RefLock.
  5727. *
  5728. * ENTRY:
  5729. * pLock (input)
  5730. * Pointer to RefLock to delete
  5731. ******************************************************************************/
  5732. VOID DeleteRefLock(PREFLOCK pLock)
  5733. {
  5734. ASSERT( pLock->RefCount > 0 );
  5735. /*
  5736. * If we are the last reference, then delete the object now
  5737. */
  5738. if ( InterlockedDecrement( &pLock->RefCount ) == 0 ) {
  5739. NtReleaseMutant( pLock->Mutex, NULL );
  5740. NtClose( pLock->Mutex );
  5741. (*pLock->pDeleteProcedure)( pLock );
  5742. /*
  5743. * Otherwise, just mark the object invalid
  5744. */
  5745. } else {
  5746. pLock->Invalid = TRUE;
  5747. NtReleaseMutant( pLock->Mutex, NULL );
  5748. }
  5749. }
  5750. BOOLEAN IsWinStationLockedByCaller(PWINSTATION pWinStation)
  5751. {
  5752. MUTANT_BASIC_INFORMATION MutantInfo;
  5753. NTSTATUS Status;
  5754. Status = NtQueryMutant( pWinStation->Lock.Mutex,
  5755. MutantBasicInformation,
  5756. &MutantInfo,
  5757. sizeof(MutantInfo),
  5758. NULL );
  5759. if ( NT_SUCCESS( Status ) )
  5760. return MutantInfo.OwnedByCaller;
  5761. return FALSE;
  5762. }
  5763. /*******************************************************************************
  5764. * WinStationEnumerateWorker
  5765. *
  5766. * Enumerate the WinStation list and return LogonIds and WinStation
  5767. * names to the caller.
  5768. *
  5769. * NOTE:
  5770. * This version only returns one entry at a time. There is no guarantee
  5771. * across calls that the list will not change, causing the users Index
  5772. * to miss an entry or get the same entry twice.
  5773. *
  5774. * ENTRY:
  5775. * pEntries (input/output)
  5776. * Pointer to number of entries to return/number actually returned
  5777. * pWin (output)
  5778. * Pointer to buffer to return entries
  5779. * pByteCount (input/output)
  5780. * Pointer to size of buffer/length of data returned in buffer
  5781. * pIndex (input/output)
  5782. * Pointer to WinStation index to return/next index
  5783. ******************************************************************************/
  5784. NTSTATUS WinStationEnumerateWorker(
  5785. PULONG pEntries,
  5786. PLOGONID pWin,
  5787. PULONG pByteCount,
  5788. PULONG pIndex)
  5789. {
  5790. PLIST_ENTRY Head, Next;
  5791. PWINSTATION pWinStation;
  5792. ULONG WinStationIndex;
  5793. ULONG MaxEntries, MaxByteCount;
  5794. NTSTATUS Status;
  5795. NTSTATUS Error = STATUS_NO_MORE_ENTRIES;
  5796. WinStationIndex = 0;
  5797. MaxEntries = *pEntries;
  5798. MaxByteCount = *pByteCount;
  5799. *pEntries = 0;
  5800. *pByteCount = 0;
  5801. Head = &WinStationListHead;
  5802. ENTERCRIT( &WinStationListLock );
  5803. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  5804. if ( *pEntries >= MaxEntries ||
  5805. *pByteCount + sizeof(LOGONID) > MaxByteCount ) {
  5806. break;
  5807. }
  5808. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  5809. if ( *pIndex == WinStationIndex ) {
  5810. (*pIndex)++; // set Index to next entry
  5811. /*
  5812. * Verify that client has QUERY access before
  5813. * returning it in the enumerate list.
  5814. * (Note that RpcCheckClientAccess only references the WinStation
  5815. * to get the LogonId, so it is safe to call this routine without
  5816. * locking the WinStation since we hold the WinStationListLock
  5817. * which prevents the WinStation from being deleted.)
  5818. */
  5819. Status = RpcCheckClientAccess( pWinStation, WINSTATION_QUERY, FALSE );
  5820. if ( NT_SUCCESS( Status ) ) {
  5821. Error = STATUS_SUCCESS;
  5822. /*
  5823. * It's possible that the LPC client can go away while we
  5824. * are processing this call. Its also possible that another
  5825. * server thread handles the LPC_PORT_CLOSED message and closes
  5826. * the port, which deletes the view memory, which is what
  5827. * pWin points to. In this case the pWin references below
  5828. * will trap. We catch this and just break out of the loop.
  5829. */
  5830. try {
  5831. pWin->LogonId = pWinStation->LogonId;
  5832. if ( pWinStation->Terminating )
  5833. pWin->State = State_Down;
  5834. else
  5835. pWin->State = pWinStation->State;
  5836. wcscpy( pWin->WinStationName, pWinStation->WinStationName );
  5837. } except( EXCEPTION_EXECUTE_HANDLER ) {
  5838. break;
  5839. }
  5840. pWin++;
  5841. (*pEntries)++;
  5842. *pByteCount += sizeof(LOGONID);
  5843. }
  5844. }
  5845. WinStationIndex++;
  5846. }
  5847. LEAVECRIT( &WinStationListLock );
  5848. return Error;
  5849. }
  5850. /*******************************************************************************
  5851. * LogonIdFromWinStationNameWorker
  5852. *
  5853. * Return the LogonId for a given WinStation name.
  5854. *
  5855. * ENTRY:
  5856. * WinStationName (input)
  5857. * name of WinStation to query
  5858. * pLogonId (output)
  5859. * Pointer to location to return LogonId
  5860. ******************************************************************************/
  5861. NTSTATUS LogonIdFromWinStationNameWorker(
  5862. PWINSTATIONNAME WinStationName,
  5863. ULONG NameSize,
  5864. PULONG pLogonId)
  5865. {
  5866. PLIST_ENTRY Head, Next;
  5867. PWINSTATION pWinStation;
  5868. NTSTATUS Status;
  5869. UINT uiLength;
  5870. // make sure we don't go beyond the end of one of the two strings
  5871. // (and work around bug #229753 : NameSize is in bytes, not a characters count)
  5872. if (NameSize > sizeof(WINSTATIONNAME)) {
  5873. uiLength = sizeof(WINSTATIONNAME)/sizeof(WCHAR);
  5874. } else {
  5875. uiLength = NameSize/sizeof(WCHAR);
  5876. }
  5877. Head = &WinStationListHead;
  5878. ENTERCRIT( &WinStationListLock );
  5879. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  5880. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  5881. if ( !_wcsnicmp( pWinStation->WinStationName, WinStationName, uiLength ) ) {
  5882. /*
  5883. * If client doesn't have QUERY access, return NOT_FOUND error
  5884. */
  5885. Status = RpcCheckClientAccess( pWinStation, WINSTATION_QUERY, FALSE );
  5886. if ( !NT_SUCCESS( Status ) )
  5887. break;
  5888. *pLogonId = pWinStation->LogonId;
  5889. LEAVECRIT( &WinStationListLock );
  5890. return( STATUS_SUCCESS );
  5891. }
  5892. }
  5893. LEAVECRIT( &WinStationListLock );
  5894. return STATUS_CTX_WINSTATION_NOT_FOUND;
  5895. }
  5896. /*******************************************************************************
  5897. * IcaWinStationNameFromLogonId
  5898. *
  5899. * Return the WinStation name for a given LogonId.
  5900. *
  5901. * ENTRY:
  5902. * LogonId (output)
  5903. * LogonId to query
  5904. * pWinStationName (input)
  5905. * pointer to location to return WinStation name
  5906. ******************************************************************************/
  5907. NTSTATUS IcaWinStationNameFromLogonId(
  5908. ULONG LogonId,
  5909. PWINSTATIONNAME pWinStationName)
  5910. {
  5911. PLIST_ENTRY Head, Next;
  5912. PWINSTATION pWinStation;
  5913. NTSTATUS Status;
  5914. Head = &WinStationListHead;
  5915. ENTERCRIT( &WinStationListLock );
  5916. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  5917. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  5918. if ( pWinStation->LogonId == LogonId ) {
  5919. /*
  5920. * If client doesn't have QUERY access, return NOT_FOUND error
  5921. */
  5922. Status = RpcCheckClientAccess( pWinStation, WINSTATION_QUERY, FALSE );
  5923. if ( !NT_SUCCESS( Status ) )
  5924. break;
  5925. wcscpy( pWinStationName, pWinStation->WinStationName );
  5926. LEAVECRIT( &WinStationListLock );
  5927. return( STATUS_SUCCESS );
  5928. }
  5929. }
  5930. LEAVECRIT( &WinStationListLock );
  5931. return STATUS_CTX_WINSTATION_NOT_FOUND;
  5932. }
  5933. NTSTATUS TerminateProcessAndWait(
  5934. HANDLE ProcessId,
  5935. HANDLE Process,
  5936. ULONG Seconds)
  5937. {
  5938. NTSTATUS Status;
  5939. ULONG mSecs;
  5940. LARGE_INTEGER Timeout;
  5941. /*
  5942. * Try to terminate the process
  5943. */
  5944. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: TerminateProcessAndWait, process=0x%x, ", ProcessId ));
  5945. Status = NtTerminateProcess( Process, STATUS_SUCCESS );
  5946. if ( !NT_SUCCESS( Status ) && Status != STATUS_PROCESS_IS_TERMINATING ) {
  5947. DBGPRINT(("Terminate=0x%x\n", Status ));
  5948. return( Status );
  5949. }
  5950. TRACE((hTrace,TC_ICASRV,TT_API1, "Terminate=0x%x, ", Status ));
  5951. /*
  5952. * Wait for the process to die
  5953. */
  5954. mSecs = Seconds * 1000;
  5955. Timeout = RtlEnlargedIntegerMultiply( mSecs, -10000 );
  5956. Status = NtWaitForSingleObject( Process, FALSE, &Timeout );
  5957. TRACE((hTrace,TC_ICASRV,TT_API1, "Wait=0x%x\n", Status ));
  5958. return Status;
  5959. }
  5960. /*****************************************************************************
  5961. * ShutdownLogoff
  5962. *
  5963. * Worker function to handle logoff notify of WinStations when
  5964. * the system is being shutdown.
  5965. *
  5966. * It is built from the code in WinStationReset
  5967. *
  5968. * ENTRY:
  5969. * Client LogonId (input)
  5970. * LogonId of the client Winstation doing the shutdown. This is so
  5971. * that he does not get reset.
  5972. * Flags (input)
  5973. * The shutdown flags.
  5974. ****************************************************************************/
  5975. NTSTATUS ShutdownLogoff(ULONG ClientLogonId, ULONG Flags)
  5976. {
  5977. PLIST_ENTRY Head, Next;
  5978. PWINSTATION pWinStation, pConsole = NULL;
  5979. PULONG Tmp;
  5980. PULONG Ids = NULL;
  5981. ULONG IdCount = 0;
  5982. ULONG IdAllocCount = 0;
  5983. NTSTATUS Status = STATUS_SUCCESS;
  5984. TRACE((hTrace,TC_ICASRV,TT_API1, "ShutdownLogoff: Called from WinStation %d Flags %x\n", ClientLogonId, Flags ));
  5985. /*
  5986. * Loop through all the WinStations getting the LogonId's
  5987. * of active Winstations
  5988. */
  5989. Head = &WinStationListHead;
  5990. ENTERCRIT( &WinStationListLock );
  5991. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  5992. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  5993. //
  5994. // take a reference on the console
  5995. //
  5996. if ( pWinStation->fOwnsConsoleTerminal ) {
  5997. if ( LockWinStationByPointer( pWinStation ) ) {
  5998. pConsole = pWinStation;
  5999. UnlockWinStation( pConsole );
  6000. }
  6001. }
  6002. //
  6003. // just skip :
  6004. // - the caller's session
  6005. // - the console (because winsrv!W32WinStationExitWindows would fail for the console)
  6006. // - the listener
  6007. //
  6008. if ( ( pWinStation->LogonId == ClientLogonId ) ||
  6009. ( pWinStation->LogonId == 0) ||
  6010. ( pWinStation->Flags & WSF_LISTEN ) ) {
  6011. // Skip this one, or it's a listen
  6012. continue;
  6013. }
  6014. if ( IdCount >= IdAllocCount ) {
  6015. // Reallocate the array
  6016. IdAllocCount += 16;
  6017. Tmp = RtlAllocateHeap( RtlProcessHeap(), 0, IdAllocCount * sizeof(ULONG) );
  6018. if ( Tmp == NULL ) {
  6019. Status = STATUS_NO_MEMORY;
  6020. if ( Ids )
  6021. RtlFreeHeap( RtlProcessHeap(), 0, Ids );
  6022. IdCount = 0;
  6023. break;
  6024. }
  6025. if ( Ids ) {
  6026. RtlCopyMemory( Tmp, Ids, IdCount*sizeof(ULONG) );
  6027. RtlFreeHeap( RtlProcessHeap(), 0, Ids );
  6028. }
  6029. Ids = Tmp;
  6030. }
  6031. // Copy the LogonId into our array
  6032. Ids[IdCount++] = pWinStation->LogonId;
  6033. }
  6034. //
  6035. // We are protected by new winstations starting up by the shutdown
  6036. // global flags.
  6037. //
  6038. // The actual WinStation reset routine will validate that the LogonId
  6039. // is still valid
  6040. //
  6041. LEAVECRIT( &WinStationListLock );
  6042. //
  6043. // see if the console is being shadowed
  6044. //
  6045. if ( pConsole ) {
  6046. RelockWinStation( pConsole );
  6047. WinStationStopAllShadows( pConsole );
  6048. ReleaseWinStation( pConsole );
  6049. }
  6050. if (IdCount !=0)
  6051. {
  6052. //
  6053. // Ids[] holds the LogonId's of valid Winstations, IdCount is the number
  6054. //
  6055. /*
  6056. * Now do the actual logout and/or reset of the WinStations.
  6057. */
  6058. if (Flags & WSD_LOGOFF) {
  6059. Status = DoForWinStationGroup( Ids, IdCount,
  6060. (LPTHREAD_START_ROUTINE) WinStationLogoff);
  6061. }
  6062. if (Flags & WSD_SHUTDOWN) {
  6063. Status = DoForWinStationGroup( Ids, IdCount,
  6064. (LPTHREAD_START_ROUTINE) WinStationShutdownReset);
  6065. }
  6066. }
  6067. return Status;
  6068. }
  6069. /*****************************************************************************
  6070. * DoForWinStationGroup
  6071. *
  6072. * Executes a function for each WinStation in the group.
  6073. * The group is passed as an array of LogonId's.
  6074. *
  6075. * ENTRY:
  6076. * Ids (input)
  6077. * Array of LogonId's of WinStations to reset
  6078. *
  6079. * IdCount (input)
  6080. * Count of LogonId's in array
  6081. *
  6082. * ThreadProc (input)
  6083. * The thread routine executed for each WinStation.
  6084. ****************************************************************************/
  6085. NTSTATUS DoForWinStationGroup(
  6086. PULONG Ids,
  6087. ULONG IdCount,
  6088. LPTHREAD_START_ROUTINE ThreadProc)
  6089. {
  6090. ULONG Index;
  6091. NTSTATUS Status;
  6092. LARGE_INTEGER Timeout;
  6093. PHANDLE ThreadHandles = NULL;
  6094. ThreadHandles = RtlAllocateHeap( RtlProcessHeap(), 0, IdCount * sizeof(HANDLE) );
  6095. if( ThreadHandles == NULL ) {
  6096. return( STATUS_NO_MEMORY );
  6097. }
  6098. /*
  6099. * Wait a max of 60 seconds for thread to exit
  6100. */
  6101. Timeout = RtlEnlargedIntegerMultiply( 60000, -10000 );
  6102. for( Index=0; Index < IdCount; Index++ ) {
  6103. //
  6104. // Here we create a thread to run the actual reset function.
  6105. // Since we are holding the lists crit sect, the threads will
  6106. // wait until we are done, then wake up when we release it
  6107. //
  6108. DWORD ThreadId;
  6109. ThreadHandles[Index] = CreateThread( NULL,
  6110. 0, // use Default stack size of the svchost process
  6111. ThreadProc,
  6112. LongToPtr( Ids[Index] ), // LogonId
  6113. THREAD_SET_INFORMATION,
  6114. &ThreadId );
  6115. if ( !ThreadHandles[Index] ) {
  6116. ThreadHandles[Index] = (HANDLE)(-1);
  6117. TRACE((hTrace,TC_ICASRV,TT_ERROR,"TERMSRV: Shutdown: Could not create thread for WinStation %d Shutdown\n", Ids[Index]));
  6118. }
  6119. }
  6120. //
  6121. // Now wait for the threads to exit. Each will reset their
  6122. // WinStation and be signal by the kernel when the thread is
  6123. // exited.
  6124. //
  6125. for (Index=0; Index < IdCount; Index++) {
  6126. if ( ThreadHandles[Index] != (HANDLE)(-1) ) {
  6127. Status = NtWaitForSingleObject(
  6128. ThreadHandles[Index],
  6129. FALSE, // Not alertable
  6130. &Timeout
  6131. );
  6132. if( Status == STATUS_TIMEOUT ) {
  6133. TRACE((hTrace,TC_ICASRV,TT_ERROR,"TERMSRV: DoForWinStationGroup: Timeout Waiting for Thread\n"));
  6134. }
  6135. else if (!NT_SUCCESS( Status ) ) {
  6136. TRACE((hTrace,TC_ICASRV,TT_ERROR,"TERMSRV: DoForWinStationGroup: Error waiting for Thread Status 0x%x\n", Status));
  6137. }
  6138. NtClose( ThreadHandles[Index] );
  6139. }
  6140. }
  6141. /* makarp:free the ThreadHandles. // #182609 */
  6142. RtlFreeHeap( RtlProcessHeap(), 0, ThreadHandles );
  6143. return STATUS_SUCCESS;
  6144. }
  6145. /*****************************************************************************
  6146. * WinStationShutdownReset
  6147. *
  6148. * Reset a WinStation due to a system shutdown. Does not re-create
  6149. * it.
  6150. *
  6151. * ENTRY:
  6152. * ThreadArg (input)
  6153. * WinStation logonId
  6154. ****************************************************************************/
  6155. ULONG WinStationShutdownReset(PVOID ThreadArg)
  6156. {
  6157. ULONG LogonId = (ULONG)(INT_PTR)ThreadArg;
  6158. PWINSTATION pWinStation;
  6159. NTSTATUS Status;
  6160. ULONG ulIndex;
  6161. BOOL bConnectDisconnectPending = TRUE;
  6162. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: ShutdownReset, LogonId=%d\n", LogonId ));
  6163. /*
  6164. * Find and lock the WinStation struct for the specified LogonId
  6165. */
  6166. pWinStation = FindWinStationById( LogonId, FALSE );
  6167. if ( pWinStation == NULL ) {
  6168. Status = STATUS_CTX_WINSTATION_NOT_FOUND;
  6169. goto done;
  6170. }
  6171. /*
  6172. * Console is a special case since it only logs off
  6173. */
  6174. if ( LogonId == 0 ) {
  6175. Status = LogoffWinStation( pWinStation, (EWX_FORCE | EWX_LOGOFF) );
  6176. ReleaseWinStation( pWinStation );
  6177. goto done;
  6178. }
  6179. /*
  6180. * Mark the winstation as being deleted.
  6181. * If a reset/delete operation is already in progress
  6182. * on this winstation, then don't proceed with the delete.
  6183. * Also if there is a Connect/disconnect pending, give it
  6184. * a chance to complete.
  6185. */
  6186. for (ulIndex=0; ulIndex < WINSTATION_WAIT_COMPLETE_RETRIES; ulIndex++) {
  6187. if ( pWinStation->Flags & (WSF_RESET | WSF_DELETE) ) {
  6188. ReleaseWinStation( pWinStation );
  6189. Status = STATUS_CTX_WINSTATION_BUSY;
  6190. goto done;
  6191. }
  6192. if ( pWinStation->Flags & (WSF_CONNECT | WSF_DISCONNECT) ) {
  6193. LARGE_INTEGER Timeout;
  6194. Timeout = RtlEnlargedIntegerMultiply( WINSTATION_WAIT_COMPLETE_DURATION, -10000 );
  6195. UnlockWinStation( pWinStation );
  6196. NtDelayExecution( FALSE, &Timeout );
  6197. if ( !RelockWinStation( pWinStation ) ) {
  6198. ReleaseWinStation( pWinStation );
  6199. Status = STATUS_SUCCESS;
  6200. goto done;
  6201. }
  6202. } else {
  6203. bConnectDisconnectPending = FALSE;
  6204. break;
  6205. }
  6206. }
  6207. if ( bConnectDisconnectPending ) {
  6208. ReleaseWinStation( pWinStation );
  6209. Status = STATUS_CTX_WINSTATION_BUSY;
  6210. goto done;
  6211. }
  6212. pWinStation->Flags |= WSF_DELETE;
  6213. /*
  6214. * If no broken reason/source have been set, then set them here.
  6215. */
  6216. if ( pWinStation->BrokenReason == 0 ) {
  6217. pWinStation->BrokenReason = Broken_Terminate;
  6218. pWinStation->BrokenSource = BrokenSource_Server;
  6219. }
  6220. /*
  6221. * Make sure this WinStation is ready to delete
  6222. */
  6223. WinStationTerminate( pWinStation );
  6224. /*
  6225. * Call the WinStationDelete worker
  6226. */
  6227. WinStationDeleteWorker( pWinStation );
  6228. Status = STATUS_SUCCESS;
  6229. done:
  6230. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: ShutdownReset, Status=0x%x\n", Status ));
  6231. ExitThread( 0 );
  6232. return Status;
  6233. }
  6234. /*****************************************************************************
  6235. * WinStationLogoff
  6236. *
  6237. * Logoff the WinStation via ExitWindows.
  6238. *
  6239. * ENTRY:
  6240. * ThreadArg (input)
  6241. * WinStation logonId
  6242. ****************************************************************************/
  6243. ULONG WinStationLogoff(PVOID ThreadArg)
  6244. {
  6245. ULONG LogonId = (ULONG)(INT_PTR)ThreadArg;
  6246. PWINSTATION pWinStation;
  6247. NTSTATUS Status;
  6248. LARGE_INTEGER Timeout;
  6249. /*
  6250. * Wait a maximum of 1 min for the session to logoff
  6251. */
  6252. Timeout = RtlEnlargedIntegerMultiply( 60000, -10000 );
  6253. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationLogoff, LogonId=%d\n", LogonId ));
  6254. /*
  6255. * Find and lock the WinStation struct for the specified LogonId
  6256. */
  6257. pWinStation = FindWinStationById( LogonId, FALSE );
  6258. if ( pWinStation == NULL ) {
  6259. Status = STATUS_CTX_WINSTATION_NOT_FOUND;
  6260. } else {
  6261. Status = LogoffWinStation( pWinStation, EWX_LOGOFF);
  6262. if (ShutdownInProgress &&
  6263. NT_SUCCESS(Status) &&
  6264. ((pWinStation->State == State_Active) ||
  6265. (pWinStation->State == State_Disconnected))) {
  6266. UnlockWinStation( pWinStation );
  6267. Status = NtWaitForSingleObject( pWinStation->InitialCommandProcess,
  6268. FALSE,
  6269. &Timeout );
  6270. RelockWinStation( pWinStation );
  6271. }
  6272. ReleaseWinStation( pWinStation );
  6273. }
  6274. TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationLogoff, Status=0x%x\n", Status ));
  6275. ExitThread( 0 );
  6276. return Status;
  6277. }
  6278. /*******************************************************************************
  6279. * ResetGroupByListener
  6280. *
  6281. * Resets all active winstations on the supplied listen name.
  6282. *
  6283. * ENTRY:
  6284. * pListenName (input)
  6285. * Type of Winstation (e.g. tcp, ipx)
  6286. ******************************************************************************/
  6287. VOID ResetGroupByListener(PWINSTATIONNAME pListenName)
  6288. {
  6289. PLIST_ENTRY Head, Next;
  6290. PWINSTATION pWinStation;
  6291. Head = &WinStationListHead;
  6292. ENTERCRIT( &WinStationListLock );
  6293. /*
  6294. * Search the list for all active WinStation with the given ListenName.
  6295. */
  6296. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  6297. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  6298. if (!wcscmp(pWinStation->ListenName, pListenName) &&
  6299. (!(pWinStation->Flags & (WSF_RESET | WSF_LISTEN)))) {
  6300. QueueWinStationReset(pWinStation->LogonId);
  6301. }
  6302. }
  6303. LEAVECRIT( &WinStationListLock );
  6304. }
  6305. NTSTATUS LogoffWinStation(PWINSTATION pWinStation, ULONG ExitWindowsFlags)
  6306. {
  6307. WINSTATION_APIMSG msg;
  6308. NTSTATUS Status = 0;
  6309. /*
  6310. * Tell the WinStation to logoff
  6311. */
  6312. msg.ApiNumber = SMWinStationExitWindows;
  6313. msg.u.ExitWindows.Flags = ExitWindowsFlags;
  6314. Status = SendWinStationCommand( pWinStation, &msg, 0 );
  6315. return Status;
  6316. }
  6317. /*****************************************************************************
  6318. *
  6319. * This section of the file contains the impementation of the digital
  6320. * certification mechanism for the Stack and WinStation Extension DLLs. This
  6321. * code is not in a separate file so that external symbols are not visible.
  6322. * All routines are declared static.
  6323. *
  6324. ****************************************************************************/
  6325. //
  6326. // For security reasons, the TRACE statements in the following routine are
  6327. // normally not included. If you want to include them, uncomment the
  6328. // SIGN_DEBUG_WINSTA #define below.
  6329. //
  6330. // #define SIGN_DEBUG_WINSTA
  6331. #include <wincrypt.h>
  6332. #include <imagehlp.h>
  6333. #include <stddef.h>
  6334. #include "../../tscert/inc/pubblob.h" // needed by certvfy.inc
  6335. #include "../../tscert/inc/certvfy.inc" // VerifyFile()
  6336. //
  6337. // The following are initialized by VfyInit.
  6338. //
  6339. static RTL_CRITICAL_SECTION VfyLock;
  6340. static WCHAR szSystemDir[ MAX_PATH + 1 ];
  6341. static WCHAR szDriverDir[ MAX_PATH + 1 ];
  6342. /*******************************************************************************
  6343. * ReportStackLoadFailure
  6344. *
  6345. * Send a StackFailed message to the WinStationApiPort.
  6346. *
  6347. * ENTRY:
  6348. * Module (input)
  6349. * Name of Module to Log Error Against
  6350. ******************************************************************************/
  6351. static NTSTATUS ReportStackLoadFailure(PWCHAR Module)
  6352. {
  6353. HANDLE h;
  6354. extern WCHAR gpszServiceName[];
  6355. h = RegisterEventSource(NULL, gpszServiceName);
  6356. if (h != NULL) {
  6357. if (!ReportEventW(h, // event log handle
  6358. EVENTLOG_ERROR_TYPE, // event type
  6359. 0, // category zero
  6360. EVENT_BAD_STACK_MODULE,// event identifier
  6361. NULL, // no user security identifier
  6362. 1, // one substitution string
  6363. 0, // no data
  6364. &Module, // pointer to string array
  6365. NULL) // pointer to data
  6366. ) {
  6367. DBGPRINT(("ReportEvent Failed %ld. Event ID=%lx module=%ws\n",GetLastError(), EVENT_BAD_STACK_MODULE, Module));
  6368. }
  6369. DeregisterEventSource(h);
  6370. } else {
  6371. DBGPRINT(("Cannot RegisterEvent Source %ld Event ID=%lx module=%ws\n",GetLastError(), EVENT_BAD_STACK_MODULE, Module));
  6372. }
  6373. return STATUS_SUCCESS;
  6374. }
  6375. /******************************************************************************
  6376. * _VerifyStackModules
  6377. * Verifies the integrity of the stack modules and the authenticity
  6378. * of the digital signature.
  6379. *
  6380. * ENTRY:
  6381. * pWinStation (input)
  6382. * Pointer to a Listen Winstation.
  6383. *
  6384. * EXIT:
  6385. * STATUS_SUCCESS - no error
  6386. * STATUS_UNSUCCESSFUL - DLL integrity check, authenticity check failed
  6387. * or registry stucture invalid
  6388. *****************************************************************************/
  6389. static NTSTATUS _VerifyStackModules(IN PWINSTATION pWinStation)
  6390. {
  6391. PWCHAR pszModulePath = NULL;
  6392. NTSTATUS Status = STATUS_SUCCESS;
  6393. DWORD KeyIndex;
  6394. DWORD Error;
  6395. #ifdef SIGN_BYPASS_OPTION
  6396. HKEY hKey;
  6397. #endif SIGN_BYPASS_OPTION
  6398. HKEY hVidKey;
  6399. HKEY hVidDriverKey;
  6400. UNICODE_STRING KeyPath;
  6401. UNICODE_STRING ValueName;
  6402. OBJECT_ATTRIBUTES ObjectAttributes;
  6403. HANDLE hServiceKey;
  6404. PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo;
  6405. ULONG ValueLength;
  6406. #define VALUE_BUFFER_SZ (sizeof(KEY_VALUE_PARTIAL_INFORMATION) + \
  6407. 256 * sizeof( WCHAR))
  6408. PCHAR pValueBuffer = NULL;
  6409. INT Entries;
  6410. DWORD dwByteCount;
  6411. PPDNAME pPdNames, p;
  6412. INT i;
  6413. DLLNAME WdDLL;
  6414. #ifdef SIGN_BYPASS_OPTION
  6415. //
  6416. // Check if Verification is to be bypassed
  6417. //
  6418. if ( RegOpenKeyEx(
  6419. HKEY_LOCAL_MACHINE,
  6420. REG_CONTROL_TSERVER L"\\BypassVerification",
  6421. 0,
  6422. KEY_READ,
  6423. &hKey ) == ERROR_SUCCESS ) {
  6424. RegCloseKey( hKey );
  6425. Status = STATUS_SUCCESS;
  6426. goto exit;
  6427. }
  6428. #endif //SIGN_BYPASS_OPTION
  6429. #ifdef SIGN_DEBUG_WINSTA
  6430. TRACE((hTrace,TC_ICASRV,TT_API1, "System Dir: %ws\n", szSystemDir ));
  6431. #endif // SIGN_DEBUG_WINSTA
  6432. // allocate memory
  6433. pszModulePath = MemAlloc( (MAX_PATH + 1) * sizeof(WCHAR) ) ;
  6434. if (pszModulePath == NULL) {
  6435. Status = STATUS_NO_MEMORY;
  6436. goto exit;
  6437. }
  6438. pValueBuffer = MemAlloc( VALUE_BUFFER_SZ );
  6439. if (pValueBuffer == NULL) {
  6440. Status = STATUS_NO_MEMORY;
  6441. goto exit;
  6442. }
  6443. //
  6444. // Verify the WSX DLL if defined
  6445. //
  6446. if ( pWinStation->Config.Wd.WsxDLL[0] != L'\0' ) {
  6447. wcscpy( pszModulePath, szSystemDir );
  6448. wcscat( pszModulePath, pWinStation->Config.Wd.WsxDLL );
  6449. wcscat( pszModulePath, L".DLL" );
  6450. #ifdef SIGN_DEBUG_WINSTA
  6451. TRACE((hTrace,TC_ICASRV,TT_API1, "==> WSX Path: %ws\n", pszModulePath ));
  6452. #endif // SIGN_DEBUG_WINSTA
  6453. if ( !VerifyFile( pszModulePath, &VfyLock ) ) {
  6454. ReportStackLoadFailure(pszModulePath);
  6455. Status = STATUS_UNSUCCESSFUL;
  6456. goto exit;
  6457. }
  6458. }
  6459. //
  6460. // Verify the WD
  6461. //
  6462. wcscpy( WdDLL, pWinStation->Config.Wd.WdDLL );
  6463. wcscpy( pszModulePath, szDriverDir );
  6464. wcscat( pszModulePath, WdDLL );
  6465. wcscat( pszModulePath, L".SYS" );
  6466. #ifdef SIGN_DEBUG_WINSTA
  6467. TRACE((hTrace,TC_ICASRV,TT_API1, "==> WD Path: %ws\n", pszModulePath ));
  6468. #endif // SIGN_DEBUG_WINSTA
  6469. if ( !VerifyFile( pszModulePath, &VfyLock ) ) {
  6470. ReportStackLoadFailure(pszModulePath);
  6471. Status = STATUS_UNSUCCESSFUL;
  6472. goto exit;
  6473. }
  6474. //
  6475. // Verify the TD which is in Pd[0]. Always defined for Listen Stack.
  6476. //
  6477. wcscpy( pszModulePath, szDriverDir );
  6478. wcscat( pszModulePath, pWinStation->Config.Pd[0].Create.PdDLL );
  6479. wcscat( pszModulePath, L".SYS" );
  6480. #ifdef SIGN_DEBUG_WINSTA
  6481. TRACE((hTrace,TC_ICASRV,TT_API1, "==> WD Path: %ws\n", pszModulePath ));
  6482. #endif // SIGN_DEBUG_WINSTA
  6483. if ( !VerifyFile( pszModulePath, &VfyLock ) ) {
  6484. ReportStackLoadFailure(pszModulePath);
  6485. Status = STATUS_UNSUCCESSFUL;
  6486. goto exit;
  6487. }
  6488. //
  6489. // Enumerate the PDs for this WD and verify all the PDs.
  6490. // Can't depend on Pd[i] for this since optional PDs won't
  6491. // be present during Listen.
  6492. //
  6493. Entries = -1;
  6494. dwByteCount = 0;
  6495. i = 0;
  6496. Error = RegPdEnumerate(
  6497. NULL,
  6498. WdDLL,
  6499. FALSE,
  6500. &i,
  6501. &Entries,
  6502. NULL,
  6503. &dwByteCount );
  6504. #ifdef SIGN_DEBUG_WINSTA
  6505. TRACE((hTrace,TC_ICASRV,TT_API1,
  6506. "RegPdEnumerate 1 complete., Entries %d, Error %d\n", Entries, Error ));
  6507. #endif // SIGN_DEBUG_WINSTA
  6508. if ( Error != ERROR_NO_MORE_ITEMS && Error != ERROR_CANTOPEN ) {
  6509. Status = STATUS_UNSUCCESSFUL;
  6510. goto exit;
  6511. }
  6512. //
  6513. // T.Share doesn't have PDs, so check if none
  6514. //
  6515. if ( Entries ) {
  6516. dwByteCount = sizeof(PDNAME) * Entries;
  6517. pPdNames = MemAlloc( dwByteCount );
  6518. if ( !pPdNames ) {
  6519. Status = STATUS_UNSUCCESSFUL;
  6520. goto exit;
  6521. }
  6522. i = 0;
  6523. Error = RegPdEnumerate(
  6524. NULL,
  6525. WdDLL,
  6526. FALSE,
  6527. &i,
  6528. &Entries,
  6529. pPdNames,
  6530. &dwByteCount );
  6531. if ( Error != ERROR_SUCCESS ) {
  6532. /* makarp #182610 */
  6533. MemFree( pPdNames );
  6534. Status = STATUS_UNSUCCESSFUL;
  6535. goto exit;
  6536. }
  6537. //
  6538. // Open up the Registry entry for each named PD and pull out the value
  6539. // of the PdDLL. This is the name of the DLL to verify.
  6540. //
  6541. for ( i = 0, p = pPdNames; i < Entries;
  6542. i++, (char*)p += sizeof(PDNAME) ) {
  6543. HKEY hPdKey;
  6544. PWCHAR pszPdDLL = NULL;
  6545. PWCHAR pszRegPath = NULL;
  6546. DWORD dwLen;
  6547. DWORD dwType;
  6548. // allocate memory
  6549. pszPdDLL = MemAlloc( (MAX_PATH+1) * sizeof(WCHAR) );
  6550. if (pszPdDLL == NULL) {
  6551. MemFree( pPdNames );
  6552. Status = STATUS_NO_MEMORY;
  6553. goto exit;
  6554. }
  6555. pszRegPath = MemAlloc( (MAX_PATH+1) * sizeof(WCHAR) );
  6556. if (pszRegPath == NULL) {
  6557. MemFree( pszPdDLL );
  6558. MemFree( pPdNames );
  6559. Status = STATUS_NO_MEMORY;
  6560. goto exit;
  6561. }
  6562. //
  6563. // Build up the Registry Path to open the PD's key
  6564. //
  6565. wcscpy( pszRegPath, WD_REG_NAME );
  6566. wcscat( pszRegPath, L"\\" );
  6567. wcscat( pszRegPath, WdDLL );
  6568. wcscat( pszRegPath, PD_REG_NAME L"\\" );
  6569. wcscat( pszRegPath, p );
  6570. #ifdef SIGN_DEBUG_WINSTA
  6571. TRACE((hTrace,TC_ICASRV,TT_API1, "PdKeyPath: %ws\n", pszRegPath ));
  6572. #endif // SIGN_DEBUG_WINSTA
  6573. if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, pszRegPath, 0, KEY_READ,
  6574. &hPdKey ) != ERROR_SUCCESS ) {
  6575. MemFree( pPdNames );
  6576. MemFree( pszPdDLL );
  6577. MemFree( pszRegPath );
  6578. Status = STATUS_UNSUCCESSFUL;
  6579. goto exit;
  6580. }
  6581. //
  6582. // Get the name of the Pd DLL.
  6583. //
  6584. dwLen = (MAX_PATH + 1) * sizeof(WCHAR) ;
  6585. if ( RegQueryValueEx( hPdKey,
  6586. WIN_PDDLL,
  6587. NULL,
  6588. &dwType,
  6589. (PCHAR) pszPdDLL,
  6590. &dwLen ) != ERROR_SUCCESS ) {
  6591. MemFree( pPdNames );
  6592. MemFree( pszPdDLL );
  6593. MemFree( pszRegPath );
  6594. // makarp:182610
  6595. RegCloseKey(hPdKey);
  6596. Status = STATUS_UNSUCCESSFUL;
  6597. goto exit;
  6598. }
  6599. // makarp:182610
  6600. RegCloseKey(hPdKey);
  6601. //
  6602. // Build path to DLL and attempt verification
  6603. //
  6604. wcscpy( pszModulePath, szDriverDir );
  6605. wcscat( pszModulePath, pszPdDLL );
  6606. wcscat( pszModulePath, L".SYS" );
  6607. #ifdef SIGN_DEBUG_WINSTA
  6608. TRACE((hTrace,TC_ICASRV,TT_API1, "==> PD Path: %ws\n", pszModulePath ));
  6609. #endif // SIGN_DEBUG_WINSTA
  6610. if ( !VerifyFile( pszModulePath, &VfyLock ) &&
  6611. GetLastError() != ERROR_CANTOPEN ) {
  6612. MemFree( pPdNames );
  6613. MemFree( pszPdDLL );
  6614. MemFree( pszRegPath );
  6615. ReportStackLoadFailure(pszModulePath);
  6616. Status = STATUS_UNSUCCESSFUL;
  6617. goto exit;
  6618. }
  6619. MemFree( pszPdDLL );
  6620. MemFree( pszRegPath );
  6621. }
  6622. MemFree( pPdNames );
  6623. }
  6624. //
  6625. // for all keys under HKLM\System\CCS\Control\Terminal Server\VIDEO
  6626. // open the subkey \Device\Video0 and use that value as
  6627. // a string to open
  6628. // \REGISTRY\Machine\System\CCS\Services\vdtw30\Device0
  6629. // DLL name is in Value "Installed Display Drivers"
  6630. //
  6631. // Open registry (LOCAL_MACHINE\System\CCS\Control\Terminal Server\VIDEO)
  6632. //
  6633. // NOTE: All video driver DLLs are verified since there isn't any simple
  6634. // method to determine which one is used for this stack.
  6635. //
  6636. if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, VIDEO_REG_NAME, 0,
  6637. KEY_ENUMERATE_SUB_KEYS, &hVidKey ) != ERROR_SUCCESS ) {
  6638. Status = STATUS_UNSUCCESSFUL;
  6639. goto exit;
  6640. }
  6641. for ( KeyIndex = 0 ;; KeyIndex++ ) { // For all VIDEO subkeys
  6642. PWCHAR pszVidDriverName = NULL;
  6643. PWCHAR pszRegPath = NULL;
  6644. PWCHAR pszDeviceKey = NULL;
  6645. PWCHAR pszServiceKey = NULL;
  6646. DWORD dwLen;
  6647. DWORD dwType;
  6648. // allocate memory
  6649. pszVidDriverName = MemAlloc( (MAX_PATH + 1) * sizeof(WCHAR) );
  6650. if (pszVidDriverName == NULL) {
  6651. Status = STATUS_NO_MEMORY;
  6652. goto exit;
  6653. }
  6654. pszRegPath = MemAlloc( (MAX_PATH + 1) * sizeof(WCHAR) );
  6655. if (pszRegPath == NULL) {
  6656. MemFree(pszVidDriverName);
  6657. Status = STATUS_NO_MEMORY;
  6658. goto exit;
  6659. }
  6660. pszDeviceKey = MemAlloc( (MAX_PATH + 1) * sizeof(WCHAR) );
  6661. if (pszDeviceKey == NULL) {
  6662. MemFree(pszVidDriverName);
  6663. MemFree(pszRegPath);
  6664. Status = STATUS_NO_MEMORY;
  6665. goto exit;
  6666. }
  6667. pszServiceKey = MemAlloc( (MAX_PATH + 1) * sizeof(WCHAR) );
  6668. if (pszServiceKey == NULL) {
  6669. MemFree(pszVidDriverName);
  6670. MemFree(pszRegPath);
  6671. MemFree(pszDeviceKey);
  6672. Status = STATUS_NO_MEMORY;
  6673. goto exit;
  6674. }
  6675. //
  6676. // Get name of VIDEO driver subkey. If end of subkeys, exit loop.
  6677. //
  6678. if ((Error = RegEnumKey( hVidKey, KeyIndex, pszVidDriverName,
  6679. MAX_PATH+1))!= ERROR_SUCCESS ){
  6680. MemFree(pszVidDriverName);
  6681. MemFree(pszRegPath);
  6682. MemFree(pszDeviceKey);
  6683. MemFree(pszServiceKey);
  6684. break; // exit for loop
  6685. }
  6686. //
  6687. // Build up the Registry Path to open the VgaCompatible Value
  6688. //
  6689. wcscpy( pszRegPath, VIDEO_REG_NAME L"\\" );
  6690. wcscat( pszRegPath, pszVidDriverName );
  6691. #ifdef SIGN_DEBUG_WINSTA
  6692. TRACE((hTrace,TC_ICASRV,TT_API1, "VidDriverKeyPath: %ws\n", pszRegPath ));
  6693. #endif // SIGN_DEBUG_WINSTA
  6694. if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, pszRegPath, 0, KEY_READ,
  6695. &hVidDriverKey ) != ERROR_SUCCESS ) {
  6696. Status = STATUS_UNSUCCESSFUL;
  6697. MemFree(pszVidDriverName);
  6698. MemFree(pszRegPath);
  6699. MemFree(pszDeviceKey);
  6700. MemFree(pszServiceKey);
  6701. goto closevidkey;
  6702. }
  6703. //
  6704. // Don't like to use constant strings, but this is the way
  6705. // WINSRV does it...
  6706. //
  6707. dwLen = (MAX_PATH + 1) * sizeof(WCHAR) ;
  6708. if ( RegQueryValueEx( hVidDriverKey,
  6709. L"VgaCompatible",
  6710. NULL,
  6711. &dwType,
  6712. (PCHAR) pszDeviceKey,
  6713. &dwLen ) != ERROR_SUCCESS ) {
  6714. RegCloseKey( hVidDriverKey );
  6715. Status = STATUS_UNSUCCESSFUL;
  6716. MemFree(pszVidDriverName);
  6717. MemFree(pszRegPath);
  6718. MemFree(pszDeviceKey);
  6719. MemFree(pszServiceKey);
  6720. goto closevidkey;
  6721. }
  6722. #ifdef SIGN_DEBUG_WINSTA
  6723. TRACE((hTrace,TC_ICASRV,TT_API1, "DeviceKey: %ws\n", pszDeviceKey ));
  6724. #endif // SIGN_DEBUG_WINSTA
  6725. dwLen = (MAX_PATH + 1) * sizeof(WCHAR);
  6726. if ( RegQueryValueEx( hVidDriverKey,
  6727. pszDeviceKey,
  6728. NULL,
  6729. &dwType,
  6730. (PCHAR) pszServiceKey,
  6731. &dwLen ) != ERROR_SUCCESS ) {
  6732. RegCloseKey( hVidDriverKey );
  6733. Status = STATUS_UNSUCCESSFUL;
  6734. MemFree(pszVidDriverName);
  6735. MemFree(pszRegPath);
  6736. MemFree(pszDeviceKey);
  6737. MemFree(pszServiceKey);
  6738. goto closevidkey;
  6739. }
  6740. RegCloseKey( hVidDriverKey );
  6741. #ifdef SIGN_DEBUG_WINSTA
  6742. TRACE((hTrace,TC_ICASRV,TT_API1, "ServiceKey: %ws\n", pszServiceKey ));
  6743. #endif // SIGN_DEBUG_WINSTA
  6744. RtlInitUnicodeString( &KeyPath, pszServiceKey );
  6745. InitializeObjectAttributes( &ObjectAttributes, &KeyPath,
  6746. OBJ_CASE_INSENSITIVE, NULL, NULL );
  6747. //
  6748. // Must use NT Registry APIs since the ServiceKey key name from
  6749. // the registry is in the form used by these APIs.
  6750. //
  6751. Status = NtOpenKey( &hServiceKey, GENERIC_READ, &ObjectAttributes );
  6752. if ( !NT_SUCCESS( Status ) ) {
  6753. DBGPRINT(( "TERMSRV: NtOpenKey failed, rc=%x\n", Status ));
  6754. Status = STATUS_UNSUCCESSFUL;
  6755. MemFree(pszVidDriverName);
  6756. MemFree(pszRegPath);
  6757. MemFree(pszDeviceKey);
  6758. MemFree(pszServiceKey);
  6759. goto closevidkey;
  6760. }
  6761. //
  6762. // Don't like to use constant strings, but this is the way
  6763. // WINSRV does it...
  6764. //
  6765. RtlInitUnicodeString( &ValueName, L"InstalledDisplayDrivers" );
  6766. KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)pValueBuffer;
  6767. Status = NtQueryValueKey( hServiceKey,
  6768. &ValueName,
  6769. KeyValuePartialInformation,
  6770. (PVOID)KeyValueInfo,
  6771. VALUE_BUFFER_SZ,
  6772. &ValueLength );
  6773. NtClose( hServiceKey );
  6774. if ( !NT_SUCCESS( Status ) ) {
  6775. Status = STATUS_UNSUCCESSFUL;
  6776. MemFree(pszVidDriverName);
  6777. MemFree(pszRegPath);
  6778. MemFree(pszDeviceKey);
  6779. MemFree(pszServiceKey);
  6780. goto closevidkey;
  6781. }
  6782. wcscpy( pszModulePath, szSystemDir );
  6783. wcscat( pszModulePath, (PWCHAR)&KeyValueInfo->Data );
  6784. wcscat( pszModulePath, L".DLL" );
  6785. #ifdef SIGN_DEBUG_WINSTA
  6786. TRACE((hTrace,TC_ICASRV,TT_API1, "==> VidDriverDLLPath: %ws\n", pszModulePath ));
  6787. #endif // SIGN_DEBUG_WINSTA
  6788. if ( !VerifyFile( pszModulePath, &VfyLock ) ) {
  6789. ReportStackLoadFailure(pszModulePath);
  6790. Status = STATUS_UNSUCCESSFUL;
  6791. MemFree(pszVidDriverName);
  6792. MemFree(pszRegPath);
  6793. MemFree(pszDeviceKey);
  6794. MemFree(pszServiceKey);
  6795. goto closevidkey;
  6796. }
  6797. MemFree(pszVidDriverName);
  6798. MemFree(pszRegPath);
  6799. MemFree(pszDeviceKey);
  6800. MemFree(pszServiceKey);
  6801. } // for all VIDEO subkeys
  6802. closevidkey:
  6803. RegCloseKey( hVidKey );
  6804. exit:
  6805. if (pszModulePath != NULL) {
  6806. MemFree(pszModulePath);
  6807. pszModulePath = NULL;
  6808. }
  6809. if (pValueBuffer != NULL) {
  6810. MemFree(pValueBuffer);
  6811. pValueBuffer = NULL;
  6812. }
  6813. return Status;
  6814. }
  6815. /*******************************************************************************
  6816. * VfyInit
  6817. * Sets up environment for Stack DLL verification.
  6818. ******************************************************************************/
  6819. NTSTATUS VfyInit()
  6820. {
  6821. GetSystemDirectory( szSystemDir, sizeof( szSystemDir )/ sizeof(WCHAR));
  6822. wcscat( szSystemDir, L"\\" );
  6823. wcscpy( szDriverDir, szSystemDir );
  6824. wcscat( szDriverDir, L"Drivers\\" );
  6825. return RtlInitializeCriticalSection(&VfyLock);
  6826. }
  6827. VOID WinstationUnloadProfile(PWINSTATION pWinStation)
  6828. {
  6829. #if 0
  6830. NTSTATUS NtStatus;
  6831. UNICODE_STRING UnicodeString;
  6832. BOOL bResult;
  6833. // if this is not the last session for this user, then we do nothing.
  6834. if (WinstationCountUserSessions(pWinStation->pProfileSid, pWinStation->LogonId) != 0) {
  6835. return;
  6836. }
  6837. // Get the user hive name from user Sid.
  6838. NtStatus = RtlConvertSidToUnicodeString( &UnicodeString, pWinStation->pProfileSid, (BOOLEAN)TRUE );
  6839. if (!NT_SUCCESS(NtStatus)) {
  6840. DBGPRINT(("TERMSRV: WinstationUnloadProfile couldn't convert Sid to string. \n"));
  6841. return;
  6842. }
  6843. // Unload the user's hive.
  6844. bResult = WinstationRegUnLoadKey(HKEY_USERS, UnicodeString.Buffer);
  6845. if (!bResult) {
  6846. DBGPRINT(("TERMSRV: WinstationUnloadProfile failed. \n"));
  6847. }
  6848. // free allocated string.
  6849. RtlFreeUnicodeString(&UnicodeString);
  6850. #endif
  6851. }
  6852. BOOL WinstationRegUnLoadKey(HKEY hKey, LPWSTR lpSubKey)
  6853. {
  6854. BOOL bResult = TRUE;
  6855. LONG error;
  6856. NTSTATUS Status;
  6857. BOOLEAN WasEnabled;
  6858. ENTERCRIT(&UserProfileLock);
  6859. //
  6860. // Enable the restore privilege
  6861. //
  6862. Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &WasEnabled);
  6863. if (NT_SUCCESS(Status)) {
  6864. error = RegUnLoadKey(hKey, lpSubKey);
  6865. if ( error != ERROR_SUCCESS) {
  6866. DBGPRINT(("TERMSRV: WinstationRegUnLoadKey RegUnLoadKey failed. \n"));
  6867. bResult = FALSE;
  6868. }
  6869. //
  6870. // Restore the privilege to its previous state
  6871. //
  6872. Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, WasEnabled, FALSE, &WasEnabled);
  6873. } else {
  6874. DBGPRINT(("TERMSRV: WinstationRegUnLoadKey adjust privilege failed. \n"));
  6875. bResult = FALSE;
  6876. }
  6877. LEAVECRIT(&UserProfileLock);
  6878. return bResult;
  6879. }
  6880. ULONG WinstationCountUserSessions(PSID pUserSid, ULONG CurrentLogonId)
  6881. {
  6882. PLIST_ENTRY Head, Next;
  6883. PWINSTATION pWinStation;
  6884. ULONG Count = 0;
  6885. PSID pSid;
  6886. Head = &WinStationListHead;
  6887. ENTERCRIT( &WinStationListLock );
  6888. // Search the list for WinStations with a matching ListenName
  6889. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  6890. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  6891. if (pWinStation->LogonId == CurrentLogonId) {
  6892. continue;
  6893. }
  6894. if (pWinStation->pUserSid != NULL) {
  6895. pSid = pWinStation->pUserSid;
  6896. } else {
  6897. pSid = pWinStation->pProfileSid;
  6898. }
  6899. if ( (pSid != NULL) && RtlEqualSid( pSid, pUserSid ) ) {
  6900. Count++;
  6901. }
  6902. }
  6903. LEAVECRIT( &WinStationListLock );
  6904. return Count;
  6905. }
  6906. PWINSTATION FindConsoleSession()
  6907. {
  6908. PLIST_ENTRY Head, Next;
  6909. PWINSTATION pWinStation;
  6910. PWINSTATION pFoundWinStation = NULL;
  6911. ULONG uCount;
  6912. Head = &WinStationListHead;
  6913. ENTERCRIT( &WinStationListLock );
  6914. /*
  6915. * Search the list for a WinStation with the Console Session.
  6916. */
  6917. searchagain:
  6918. uCount = 0;
  6919. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  6920. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  6921. if ( pWinStation->fOwnsConsoleTerminal) {
  6922. uCount++;
  6923. /*
  6924. * Now try to lock the WinStation.
  6925. */
  6926. if (pFoundWinStation == NULL){
  6927. if ( !LockRefLock( &pWinStation->Lock ) )
  6928. goto searchagain;
  6929. pFoundWinStation = pWinStation;
  6930. }
  6931. #if DBG
  6932. #else
  6933. break;
  6934. #endif
  6935. }
  6936. }
  6937. ASSERT((uCount <= 1));
  6938. /*
  6939. * If the WinStationList lock should not be held, then release it now.
  6940. */
  6941. LEAVECRIT( &WinStationListLock );
  6942. return pFoundWinStation;
  6943. }
  6944. PWINSTATION FindIdleSessionZero()
  6945. {
  6946. PLIST_ENTRY Head, Next;
  6947. PWINSTATION pWinStation;
  6948. PWINSTATION pFoundWinStation = NULL;
  6949. ULONG uCount;
  6950. Head = &WinStationListHead;
  6951. ENTERCRIT( &WinStationListLock );
  6952. /*
  6953. * Search the list for a WinStation with the Console Session.
  6954. */
  6955. searchagain:
  6956. uCount = 0;
  6957. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  6958. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  6959. if (pWinStation->LogonId == 0) {
  6960. uCount++;
  6961. /*
  6962. * Now try to lock the WinStation.
  6963. */
  6964. if (pFoundWinStation == NULL){
  6965. if ( !LockRefLock( &pWinStation->Lock ) )
  6966. goto searchagain;
  6967. pFoundWinStation = pWinStation;
  6968. }
  6969. #if DBG
  6970. #else
  6971. break;
  6972. #endif
  6973. }
  6974. }
  6975. ASSERT((uCount <= 1));
  6976. /*
  6977. * If the WinStationList lock should not be held, then release it now.
  6978. */
  6979. LEAVECRIT( &WinStationListLock );
  6980. if (pFoundWinStation != NULL) {
  6981. if ((pFoundWinStation->State == State_Disconnected) &&
  6982. (!pFoundWinStation->Flags) &&
  6983. (pFoundWinStation->UserName[0] == L'\0') ) {
  6984. return pFoundWinStation;
  6985. } else {
  6986. ReleaseWinStation(pFoundWinStation);
  6987. }
  6988. }
  6989. return NULL;
  6990. }
  6991. BOOLEAN WinStationCheckConsoleSession(VOID)
  6992. {
  6993. PWINSTATION pWinStation;
  6994. // Check if there already is a console session
  6995. pWinStation = FindConsoleSession();
  6996. if (pWinStation != NULL) {
  6997. ReleaseWinStation(pWinStation);
  6998. return TRUE;
  6999. } else {
  7000. if (gConsoleCreationDisable > 0) {
  7001. return FALSE;
  7002. }
  7003. }
  7004. //
  7005. // See if we can use a disconnected session zero that is not in use
  7006. //
  7007. if (ConsoleReconnectInfo.hStack != NULL) {
  7008. pWinStation = FindIdleSessionZero();
  7009. if (gConsoleCreationDisable > 0) {
  7010. if (pWinStation != NULL) {
  7011. ReleaseWinStation(pWinStation);
  7012. }
  7013. return FALSE;
  7014. }
  7015. if (pWinStation != NULL) {
  7016. NTSTATUS Status;
  7017. pWinStation->Flags |= WSF_CONNECT;
  7018. Status = WinStationDoReconnect(pWinStation, &ConsoleReconnectInfo);
  7019. pWinStation->Flags &= ~WSF_CONNECT;
  7020. ReleaseWinStation(pWinStation);
  7021. if (NT_SUCCESS(Status)) {
  7022. RtlZeroMemory(&ConsoleReconnectInfo,sizeof(RECONNECT_INFO));
  7023. return TRUE;
  7024. }else{
  7025. CleanupReconnect(&ConsoleReconnectInfo);
  7026. RtlZeroMemory(&ConsoleReconnectInfo,sizeof(RECONNECT_INFO));
  7027. }
  7028. }
  7029. }
  7030. // We nead to create a new session to connect to the Console
  7031. pWinStation = FindIdleWinStation();
  7032. if (pWinStation == NULL) {
  7033. WinStationCreateWorker( NULL, NULL );
  7034. pWinStation = FindIdleWinStation();
  7035. if (pWinStation == NULL) {
  7036. DBGPRINT(("TERMSRV: WinStationCheckConsoleSession - Fail to get an idle session\n"));
  7037. return FALSE;
  7038. }
  7039. }
  7040. if (gConsoleCreationDisable > 0) {
  7041. ReleaseWinStation(pWinStation);
  7042. return FALSE;
  7043. }
  7044. // Set the session as owning the Console and wakeup the WaitForConnectWorker
  7045. // Actually there is more to do than that and I will need to process LLS licensing here.
  7046. pWinStation->fOwnsConsoleTerminal = TRUE;
  7047. pWinStation->State = State_ConnectQuery;
  7048. pWinStation->Flags &= ~WSF_IDLE;
  7049. wcscpy(pWinStation->WinStationName, L"Console");
  7050. CleanupReconnect(&ConsoleReconnectInfo);
  7051. RtlZeroMemory(&ConsoleReconnectInfo,sizeof(RECONNECT_INFO));
  7052. NtSetEvent( pWinStation->ConnectEvent, NULL );
  7053. ReleaseWinStation(pWinStation);
  7054. // If necessary, create another idle WinStation to replace the one being connected
  7055. NtSetEvent(WinStationIdleControlEvent, NULL);
  7056. return TRUE;
  7057. }
  7058. /******************************************************************************
  7059. * Tells win32k to load the console shadow mirroring driver
  7060. *
  7061. * ENTRY:
  7062. * pWinStation (input)
  7063. * Pointer to the console Winstation.
  7064. * pClientConfig (input)
  7065. * Pointer to the configuration of the shadow client.
  7066. *
  7067. * EXIT:
  7068. * STATUS_SUCCESS - no error
  7069. * STATUS_xxx - error
  7070. *****************************************************************************/
  7071. NTSTATUS ConsoleShadowStart( IN PWINSTATION pWinStation,
  7072. IN PWINSTATIONCONFIG2 pClientConfig,
  7073. IN PVOID pModuleData,
  7074. IN ULONG ModuleDataLength)
  7075. {
  7076. NTSTATUS Status;
  7077. WINSTATION_APIMSG WMsg;
  7078. ULONG ReturnLength;
  7079. TRACE((hTrace, TC_ICASRV, TT_API1, "CONSOLE REMOTING: LOAD DD\n"));
  7080. Status = NtCreateEvent( &pWinStation->ShadowDisplayChangeEvent, EVENT_ALL_ACCESS,
  7081. NULL, NotificationEvent, FALSE );
  7082. if ( !NT_SUCCESS( Status) ) {
  7083. goto badevent;
  7084. }
  7085. Status = NtDuplicateObject( NtCurrentProcess(),
  7086. pWinStation->ShadowDisplayChangeEvent,
  7087. pWinStation->WindowsSubSysProcess,
  7088. &WMsg.u.DoConnect.hDisplayChangeEvent,
  7089. 0,
  7090. 0,
  7091. DUPLICATE_SAME_ACCESS );
  7092. if ( !NT_SUCCESS( Status ) ) {
  7093. DBGPRINT(( "TERMSRV: ConsoleShadowStart, LogonId=%d, NtDuplicateObject 0x%x\n",
  7094. pWinStation->LogonId, Status ));
  7095. goto badevent;
  7096. }
  7097. /*
  7098. * Read Wd, Cd and Pd configuration data from registry
  7099. */
  7100. Status = RegConsoleShadowQuery( SERVERNAME_CURRENT,
  7101. pWinStation->WinStationName,
  7102. pClientConfig->Wd.WdPrefix,
  7103. &pWinStation->Config,
  7104. sizeof(WINSTATIONCONFIG2),
  7105. &ReturnLength );
  7106. if ( !NT_SUCCESS(Status) ) {
  7107. goto badconfig;
  7108. }
  7109. /*
  7110. * Build the Console Stack.
  7111. * We need this special stack for the Console Shadow.
  7112. */
  7113. Status = IcaOpen( &pWinStation->hIca );
  7114. if ( !NT_SUCCESS( Status ) ) {
  7115. DBGPRINT(( "TERMSRV IcaOpen for console stack : Error 0x%x from IcaOpen, last error %d\n",
  7116. Status, GetLastError() ));
  7117. goto badopen;
  7118. }
  7119. Status = IcaStackOpen( pWinStation->hIca, Stack_Console,
  7120. (PROC)WsxStackIoControl, pWinStation, &pWinStation->hStack );
  7121. if ( !NT_SUCCESS( Status ) ) {
  7122. DBGPRINT(( "TERMSRV IcaOpen for console stack : Error 0x%x from IcaOpen, last error %d\n",
  7123. Status, GetLastError() ));
  7124. goto badstackopen;
  7125. }
  7126. DBGPRINT(("WinStationStart: pushing stack for console...\n"));
  7127. /*
  7128. * Load and initialize the WinStation extensions
  7129. */
  7130. pWinStation->pWsx = FindWinStationExtensionDll(
  7131. pWinStation->Config.Wd.WsxDLL,
  7132. pWinStation->Config.Wd.WdFlag );
  7133. if ( pWinStation->pWsx &&
  7134. pWinStation->pWsx->pWsxWinStationInitialize )
  7135. {
  7136. Status = pWinStation->pWsx->pWsxWinStationInitialize(
  7137. &pWinStation->pWsxContext );
  7138. }
  7139. if ( !NT_SUCCESS( Status ) ) {
  7140. DBGPRINT(( "TERMSRV IcaOpen for console stack : Error 0x%x from IcaOpen, last error %d\n",
  7141. Status, GetLastError() ));
  7142. goto badextension;
  7143. }
  7144. /*
  7145. * Load the stack
  7146. */
  7147. Status = IcaPushConsoleStack( (HANDLE)(pWinStation->hStack),
  7148. pWinStation->WinStationName,
  7149. &pWinStation->Config,
  7150. pModuleData,
  7151. ModuleDataLength);
  7152. if ( !NT_SUCCESS( Status ) ) {
  7153. DBGPRINT(( "TERMSRV IcaOpen for console stack : Error 0x%x from IcaOpen, last error %d\n",
  7154. Status, GetLastError() ));
  7155. goto badpushstack;
  7156. }
  7157. DBGPRINT(("WinStationStart: pushed stack for console\n"));
  7158. /*
  7159. * This code is based on that in WaitForConnectWorker (see wait.c)
  7160. */
  7161. if ( !(pWinStation->pWsx) ||
  7162. !(pWinStation->pWsx->pWsxInitializeClientData) ) {
  7163. DBGPRINT(( "TERMSRV: ConsoleShadowStart, LogonId=%d, No pWsxInitializeClientData\n" ));
  7164. Status = STATUS_CTX_SHADOW_INVALID;
  7165. goto done;
  7166. }
  7167. pWinStation->State = State_Idle;
  7168. /*
  7169. * Open the beep channel (if not already) and duplicate it.
  7170. * This is one channel that both CSR and ICASRV have open.
  7171. */
  7172. Status = IcaChannelOpen( pWinStation->hIca,
  7173. Channel_Beep,
  7174. NULL,
  7175. &pWinStation->hIcaBeepChannel );
  7176. if ( !NT_SUCCESS( Status ) ) {
  7177. DBGPRINT(( "TERMSRV: ConsoleShadowStart, LogonId=%d, IcaChannelOpen 0x%x\n",
  7178. pWinStation->LogonId, Status ));
  7179. goto done;
  7180. }
  7181. Status = NtDuplicateObject( NtCurrentProcess(),
  7182. pWinStation->hIcaBeepChannel,
  7183. pWinStation->WindowsSubSysProcess,
  7184. &WMsg.u.DoConnect.hIcaBeepChannel,
  7185. 0,
  7186. 0,
  7187. DUPLICATE_SAME_ACCESS );
  7188. if ( !NT_SUCCESS( Status ) ) {
  7189. DBGPRINT(( "TERMSRV: ConsoleShadowStart, LogonId=%d, NtDuplicateObject 0x%x\n",
  7190. pWinStation->LogonId, Status ));
  7191. goto done;
  7192. }
  7193. /*
  7194. * Open the thinwire channel (if not already) and duplicate it.
  7195. * This is one channel that both CSR and ICASRV have open.
  7196. */
  7197. Status = IcaChannelOpen( pWinStation->hIca,
  7198. Channel_Virtual,
  7199. VIRTUAL_THINWIRE,
  7200. &pWinStation->hIcaThinwireChannel );
  7201. if ( !NT_SUCCESS( Status ) ) {
  7202. DBGPRINT(( "TERMSRV: ConsoleShadowStart, LogonId=%d, IcaChannelOpen 0x%x\n",
  7203. pWinStation->LogonId, Status ));
  7204. goto done;
  7205. }
  7206. Status = NtDuplicateObject( NtCurrentProcess(),
  7207. pWinStation->hIcaThinwireChannel,
  7208. pWinStation->WindowsSubSysProcess,
  7209. &WMsg.u.DoConnect.hIcaThinwireChannel,
  7210. 0,
  7211. 0,
  7212. DUPLICATE_SAME_ACCESS );
  7213. if ( !NT_SUCCESS( Status ) ) {
  7214. DBGPRINT(( "TERMSRV: ConsoleShadowStart, LogonId=%d, NtDuplicateObject 0x%x\n",
  7215. pWinStation->LogonId, Status ));
  7216. goto done;
  7217. }
  7218. Status = IcaChannelIoControl( pWinStation->hIcaThinwireChannel,
  7219. IOCTL_ICA_CHANNEL_ENABLE_SHADOW,
  7220. NULL, 0, NULL, 0, NULL );
  7221. ASSERT( NT_SUCCESS( Status ) );
  7222. /*
  7223. * Video channel
  7224. */
  7225. Status = WinStationOpenChannel( pWinStation->hIca,
  7226. pWinStation->WindowsSubSysProcess,
  7227. Channel_Video,
  7228. NULL,
  7229. &WMsg.u.DoConnect.hIcaVideoChannel );
  7230. if ( !NT_SUCCESS( Status ) ) {
  7231. DBGPRINT(( "TERMSRV: ConsoleShadowStart, LogonId=%d, NtDuplicateObject 0x%x\n",
  7232. pWinStation->LogonId, Status ));
  7233. goto done;
  7234. }
  7235. /*
  7236. * Keyboard channel
  7237. */
  7238. Status = WinStationOpenChannel( pWinStation->hIca,
  7239. pWinStation->WindowsSubSysProcess,
  7240. Channel_Keyboard,
  7241. NULL,
  7242. &WMsg.u.DoConnect.hIcaKeyboardChannel );
  7243. if ( !NT_SUCCESS( Status ) ) {
  7244. DBGPRINT(( "TERMSRV: ConsoleShadowStart, LogonId=%d, NtDuplicateObject 0x%x\n",
  7245. pWinStation->LogonId, Status ));
  7246. goto done;
  7247. }
  7248. /*
  7249. * Mouse channel
  7250. */
  7251. Status = WinStationOpenChannel( pWinStation->hIca,
  7252. pWinStation->WindowsSubSysProcess,
  7253. Channel_Mouse,
  7254. NULL,
  7255. &WMsg.u.DoConnect.hIcaMouseChannel );
  7256. if ( !NT_SUCCESS( Status ) ) {
  7257. DBGPRINT(( "TERMSRV: ConsoleShadowStart, LogonId=%d, NtDuplicateObject 0x%x\n",
  7258. pWinStation->LogonId, Status ));
  7259. goto done;
  7260. }
  7261. /*
  7262. * Command channel
  7263. */
  7264. Status = WinStationOpenChannel( pWinStation->hIca,
  7265. pWinStation->WindowsSubSysProcess,
  7266. Channel_Command,
  7267. NULL,
  7268. &WMsg.u.DoConnect.hIcaCommandChannel );
  7269. if ( !NT_SUCCESS( Status ) ) {
  7270. DBGPRINT(( "TERMSRV: ConsoleShadowStart, LogonId=%d, NtDuplicateObject 0x%x\n",
  7271. pWinStation->LogonId, Status ));
  7272. goto done;
  7273. }
  7274. /*
  7275. * Secure any virtual channels
  7276. */
  7277. VirtualChannelSecurity( pWinStation );
  7278. /*
  7279. * Get the client data
  7280. */
  7281. Status = pWinStation->pWsx->pWsxInitializeClientData(
  7282. pWinStation->pWsxContext,
  7283. pWinStation->hStack,
  7284. pWinStation->hIca,
  7285. pWinStation->hIcaThinwireChannel,
  7286. pWinStation->VideoModuleName,
  7287. sizeof(pWinStation->VideoModuleName),
  7288. &pWinStation->Config.Config.User,
  7289. &pWinStation->Client.HRes,
  7290. &pWinStation->Client.VRes,
  7291. &pWinStation->Client.ColorDepth,
  7292. &WMsg.u.DoConnect );
  7293. if ( !NT_SUCCESS( Status ) ) {
  7294. DBGPRINT(( "TERMSRV: ConsoleShadowStart, LogonId=%d, InitializeClientData failed 0x%x\n",
  7295. pWinStation->LogonId, Status ));
  7296. goto done;
  7297. }
  7298. /*
  7299. * Store WinStation name in connect msg
  7300. */
  7301. RtlCopyMemory( WMsg.u.DoConnect.WinStationName,
  7302. pWinStation->WinStationName,
  7303. sizeof(WINSTATIONNAME) );
  7304. /*
  7305. * Save screen resolution, and color depth
  7306. */
  7307. WMsg.u.DoConnect.HRes = pWinStation->Client.HRes;
  7308. WMsg.u.DoConnect.VRes = pWinStation->Client.VRes;
  7309. /*
  7310. * Translate the color to the format excpected in winsrv
  7311. */
  7312. switch(pWinStation->Client.ColorDepth){
  7313. case 1:
  7314. WMsg.u.DoConnect.ColorDepth=4 ; // 16 colors
  7315. break;
  7316. case 2:
  7317. WMsg.u.DoConnect.ColorDepth=8 ; // 256
  7318. break;
  7319. case 4:
  7320. WMsg.u.DoConnect.ColorDepth= 16;// 64K
  7321. break;
  7322. case 8:
  7323. WMsg.u.DoConnect.ColorDepth= 24;// 16M
  7324. break;
  7325. #define DC_HICOLOR
  7326. #ifdef DC_HICOLOR
  7327. case 16:
  7328. WMsg.u.DoConnect.ColorDepth= 15;// 32K
  7329. break;
  7330. #endif
  7331. default:
  7332. WMsg.u.DoConnect.ColorDepth=8 ;
  7333. break;
  7334. }
  7335. /*
  7336. * Tell Win32 about the connection
  7337. */
  7338. WMsg.ApiNumber = SMWinStationDoConnect;
  7339. WMsg.u.DoConnect.ConsoleShadowFlag = TRUE;
  7340. Status = SendWinStationCommand( pWinStation, &WMsg, 60 );
  7341. TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: SMWinStationDoConnect %d Status=0x%x\n",
  7342. pWinStation->LogonId, Status));
  7343. if ( !NT_SUCCESS( Status ) ) {
  7344. DBGPRINT(( "TERMSRV: ConsoleShadowStart, LogonId=%d, SendWinStationCommand failed 0x%x\n",
  7345. pWinStation->LogonId, Status ));
  7346. goto done;
  7347. }
  7348. /*
  7349. * This flag is important: without it, WinStationDoDisconnect won't let
  7350. * Win32k know about the disconnection, so it can't unload the chained DD.
  7351. */
  7352. pWinStation->StateFlags |= WSF_ST_CONNECTED_TO_CSRSS;
  7353. /*
  7354. * Set connect time
  7355. */
  7356. NtQuerySystemTime( &pWinStation->ConnectTime );
  7357. /*
  7358. * no need for logon timers here - we don't want to
  7359. * stop the console session!
  7360. */
  7361. TRACE((hTrace, TC_ICASRV, TT_API1, "CONSOLE REMOTING: LOADED DD\n"));
  7362. pWinStation->State = State_Active;
  7363. return Status;
  7364. /*
  7365. * Error paths:
  7366. */
  7367. done:
  7368. // to undo the push stack, does the IcaStackClose below suffice?
  7369. pWinStation->State = State_Active;
  7370. badpushstack:
  7371. if (pWinStation->pWsxContext) {
  7372. if ( pWinStation->pWsx &&
  7373. pWinStation->pWsx->pWsxWinStationRundown ) {
  7374. pWinStation->pWsx->pWsxWinStationRundown( pWinStation->pWsxContext );
  7375. }
  7376. pWinStation->pWsxContext = NULL;
  7377. }
  7378. badextension:
  7379. pWinStation->pWsx = NULL;
  7380. IcaStackClose( pWinStation->hStack );
  7381. badstackopen:
  7382. IcaClose( pWinStation->hIca );
  7383. badopen:
  7384. pWinStation->Config = gConsoleConfig;
  7385. badconfig:
  7386. NtClose(pWinStation->ShadowDisplayChangeEvent);
  7387. pWinStation->ShadowDisplayChangeEvent = NULL;
  7388. badevent:
  7389. return Status;
  7390. }
  7391. /******************************************************************************
  7392. * Tells win32k to unload the console shadow mirroring driver
  7393. *
  7394. * ENTRY:
  7395. * pWinStation (input)
  7396. * Pointer to the console Winstation.
  7397. *
  7398. * EXIT:
  7399. * STATUS_SUCCESS - no error
  7400. * STATUS_xxx - error
  7401. *****************************************************************************/
  7402. NTSTATUS ConsoleShadowStop(PWINSTATION pWinStation)
  7403. {
  7404. WINSTATION_APIMSG ConsoleShadowStopMsg;
  7405. NTSTATUS Status;
  7406. /*
  7407. * Tell Win32k to unload the chained DD
  7408. */
  7409. ConsoleShadowStopMsg.ApiNumber = SMWinStationDoDisconnect;
  7410. ConsoleShadowStopMsg.u.DoDisconnect.ConsoleShadowFlag = TRUE;
  7411. Status = SendWinStationCommand( pWinStation, &ConsoleShadowStopMsg, 600 );
  7412. if ( !NT_SUCCESS(Status) ) {
  7413. TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: CSR ConsoleShadowStop failed LogonId=%d Status=0x%x\n",
  7414. pWinStation->LogonId, Status ));
  7415. }
  7416. /*
  7417. * No matter what happened, everything must be undone.
  7418. */
  7419. if (pWinStation->pWsxContext) {
  7420. if ( pWinStation->pWsx &&
  7421. pWinStation->pWsx->pWsxWinStationRundown ) {
  7422. pWinStation->pWsx->pWsxWinStationRundown( pWinStation->pWsxContext );
  7423. }
  7424. pWinStation->pWsxContext = NULL;
  7425. }
  7426. pWinStation->pWsx = NULL;
  7427. IcaStackClose( pWinStation->hStack );
  7428. IcaClose( pWinStation->hIca );
  7429. /*
  7430. * Restore console config.
  7431. */
  7432. pWinStation->Config = gConsoleConfig;
  7433. NtClose(pWinStation->ShadowDisplayChangeEvent);
  7434. pWinStation->ShadowDisplayChangeEvent = NULL;
  7435. return Status;
  7436. }
  7437. ULONG CodePairs[] = {
  7438. // Very general NT Status
  7439. STATUS_SUCCESS, NO_ERROR,
  7440. STATUS_NO_MEMORY, ERROR_NOT_ENOUGH_MEMORY,
  7441. STATUS_ACCESS_DENIED, ERROR_ACCESS_DENIED,
  7442. STATUS_INSUFFICIENT_RESOURCES, ERROR_NO_SYSTEM_RESOURCES,
  7443. STATUS_BUFFER_TOO_SMALL, ERROR_INSUFFICIENT_BUFFER,
  7444. STATUS_OBJECT_NAME_NOT_FOUND, ERROR_FILE_NOT_FOUND,
  7445. STATUS_NOT_SUPPORTED, ERROR_NOT_SUPPORTED,
  7446. // RPC specific Status
  7447. RPC_NT_SERVER_UNAVAILABLE, RPC_S_SERVER_UNAVAILABLE,
  7448. RPC_NT_INVALID_STRING_BINDING, RPC_S_INVALID_STRING_BINDING,
  7449. RPC_NT_WRONG_KIND_OF_BINDING, RPC_S_WRONG_KIND_OF_BINDING,
  7450. RPC_NT_PROTSEQ_NOT_SUPPORTED, RPC_S_PROTSEQ_NOT_SUPPORTED,
  7451. RPC_NT_INVALID_RPC_PROTSEQ, RPC_S_INVALID_RPC_PROTSEQ,
  7452. RPC_NT_INVALID_STRING_UUID, RPC_S_INVALID_STRING_UUID,
  7453. RPC_NT_INVALID_ENDPOINT_FORMAT, RPC_S_INVALID_ENDPOINT_FORMAT,
  7454. RPC_NT_INVALID_NET_ADDR, RPC_S_INVALID_NET_ADDR,
  7455. RPC_NT_NO_ENDPOINT_FOUND, RPC_S_NO_ENDPOINT_FOUND,
  7456. RPC_NT_INVALID_TIMEOUT, RPC_S_INVALID_TIMEOUT,
  7457. RPC_NT_OBJECT_NOT_FOUND, RPC_S_OBJECT_NOT_FOUND,
  7458. RPC_NT_ALREADY_REGISTERED, RPC_S_ALREADY_REGISTERED,
  7459. RPC_NT_TYPE_ALREADY_REGISTERED, RPC_S_TYPE_ALREADY_REGISTERED,
  7460. RPC_NT_ALREADY_LISTENING, RPC_S_ALREADY_LISTENING,
  7461. RPC_NT_NO_PROTSEQS_REGISTERED, RPC_S_NO_PROTSEQS_REGISTERED,
  7462. RPC_NT_NOT_LISTENING, RPC_S_NOT_LISTENING,
  7463. RPC_NT_UNKNOWN_MGR_TYPE, RPC_S_UNKNOWN_MGR_TYPE,
  7464. RPC_NT_UNKNOWN_IF, RPC_S_UNKNOWN_IF,
  7465. RPC_NT_NO_BINDINGS, RPC_S_NO_BINDINGS,
  7466. RPC_NT_NO_MORE_BINDINGS, RPC_S_NO_MORE_BINDINGS,
  7467. RPC_NT_NO_PROTSEQS, RPC_S_NO_PROTSEQS,
  7468. RPC_NT_CANT_CREATE_ENDPOINT, RPC_S_CANT_CREATE_ENDPOINT,
  7469. RPC_NT_OUT_OF_RESOURCES, RPC_S_OUT_OF_RESOURCES,
  7470. RPC_NT_SERVER_TOO_BUSY, RPC_S_SERVER_TOO_BUSY,
  7471. RPC_NT_INVALID_NETWORK_OPTIONS, RPC_S_INVALID_NETWORK_OPTIONS,
  7472. RPC_NT_NO_CALL_ACTIVE, RPC_S_NO_CALL_ACTIVE,
  7473. RPC_NT_CALL_FAILED, RPC_S_CALL_FAILED,
  7474. RPC_NT_CALL_FAILED_DNE, RPC_S_CALL_FAILED_DNE,
  7475. RPC_NT_PROTOCOL_ERROR, RPC_S_PROTOCOL_ERROR,
  7476. RPC_NT_UNSUPPORTED_TRANS_SYN, RPC_S_UNSUPPORTED_TRANS_SYN,
  7477. RPC_NT_UNSUPPORTED_TYPE, RPC_S_UNSUPPORTED_TYPE,
  7478. RPC_NT_INVALID_TAG, RPC_S_INVALID_TAG,
  7479. RPC_NT_INVALID_BOUND, RPC_S_INVALID_BOUND,
  7480. RPC_NT_NO_ENTRY_NAME, RPC_S_NO_ENTRY_NAME,
  7481. RPC_NT_INVALID_NAME_SYNTAX, RPC_S_INVALID_NAME_SYNTAX,
  7482. RPC_NT_UNSUPPORTED_NAME_SYNTAX, RPC_S_UNSUPPORTED_NAME_SYNTAX,
  7483. RPC_NT_UUID_NO_ADDRESS, RPC_S_UUID_NO_ADDRESS,
  7484. RPC_NT_DUPLICATE_ENDPOINT, RPC_S_DUPLICATE_ENDPOINT,
  7485. RPC_NT_UNKNOWN_AUTHN_TYPE, RPC_S_UNKNOWN_AUTHN_TYPE,
  7486. RPC_NT_MAX_CALLS_TOO_SMALL, RPC_S_MAX_CALLS_TOO_SMALL,
  7487. RPC_NT_STRING_TOO_LONG, RPC_S_STRING_TOO_LONG,
  7488. RPC_NT_PROTSEQ_NOT_FOUND, RPC_S_PROTSEQ_NOT_FOUND,
  7489. RPC_NT_PROCNUM_OUT_OF_RANGE, RPC_S_PROCNUM_OUT_OF_RANGE,
  7490. RPC_NT_BINDING_HAS_NO_AUTH, RPC_S_BINDING_HAS_NO_AUTH,
  7491. RPC_NT_UNKNOWN_AUTHN_SERVICE, RPC_S_UNKNOWN_AUTHN_SERVICE,
  7492. RPC_NT_UNKNOWN_AUTHN_LEVEL, RPC_S_UNKNOWN_AUTHN_LEVEL,
  7493. RPC_NT_INVALID_AUTH_IDENTITY, RPC_S_INVALID_AUTH_IDENTITY,
  7494. RPC_NT_UNKNOWN_AUTHZ_SERVICE, RPC_S_UNKNOWN_AUTHZ_SERVICE,
  7495. RPC_NT_NOTHING_TO_EXPORT, RPC_S_NOTHING_TO_EXPORT,
  7496. RPC_NT_INCOMPLETE_NAME, RPC_S_INCOMPLETE_NAME,
  7497. RPC_NT_INVALID_VERS_OPTION, RPC_S_INVALID_VERS_OPTION,
  7498. RPC_NT_NO_MORE_MEMBERS, RPC_S_NO_MORE_MEMBERS,
  7499. RPC_NT_NOT_ALL_OBJS_UNEXPORTED, RPC_S_NOT_ALL_OBJS_UNEXPORTED,
  7500. RPC_NT_INTERFACE_NOT_FOUND, RPC_S_INTERFACE_NOT_FOUND,
  7501. RPC_NT_ENTRY_ALREADY_EXISTS, RPC_S_ENTRY_ALREADY_EXISTS,
  7502. RPC_NT_ENTRY_NOT_FOUND, RPC_S_ENTRY_NOT_FOUND,
  7503. RPC_NT_NAME_SERVICE_UNAVAILABLE, RPC_S_NAME_SERVICE_UNAVAILABLE,
  7504. RPC_NT_INVALID_NAF_ID, RPC_S_INVALID_NAF_ID,
  7505. RPC_NT_CANNOT_SUPPORT, RPC_S_CANNOT_SUPPORT,
  7506. RPC_NT_NO_CONTEXT_AVAILABLE, RPC_S_NO_CONTEXT_AVAILABLE,
  7507. RPC_NT_INTERNAL_ERROR, RPC_S_INTERNAL_ERROR,
  7508. RPC_NT_ZERO_DIVIDE, RPC_S_ZERO_DIVIDE,
  7509. RPC_NT_ADDRESS_ERROR, RPC_S_ADDRESS_ERROR,
  7510. RPC_NT_FP_DIV_ZERO, RPC_S_FP_DIV_ZERO,
  7511. RPC_NT_FP_UNDERFLOW, RPC_S_FP_UNDERFLOW,
  7512. RPC_NT_FP_OVERFLOW, RPC_S_FP_OVERFLOW,
  7513. RPC_NT_NO_MORE_ENTRIES, RPC_X_NO_MORE_ENTRIES,
  7514. RPC_NT_SS_CHAR_TRANS_OPEN_FAIL, RPC_X_SS_CHAR_TRANS_OPEN_FAIL,
  7515. RPC_NT_SS_CHAR_TRANS_SHORT_FILE, RPC_X_SS_CHAR_TRANS_SHORT_FILE,
  7516. RPC_NT_SS_CONTEXT_MISMATCH, ERROR_INVALID_HANDLE,
  7517. RPC_NT_SS_CONTEXT_DAMAGED, RPC_X_SS_CONTEXT_DAMAGED,
  7518. RPC_NT_SS_HANDLES_MISMATCH, RPC_X_SS_HANDLES_MISMATCH,
  7519. RPC_NT_SS_CANNOT_GET_CALL_HANDLE, RPC_X_SS_CANNOT_GET_CALL_HANDLE,
  7520. RPC_NT_NULL_REF_POINTER, RPC_X_NULL_REF_POINTER,
  7521. RPC_NT_ENUM_VALUE_OUT_OF_RANGE, RPC_X_ENUM_VALUE_OUT_OF_RANGE,
  7522. RPC_NT_BYTE_COUNT_TOO_SMALL, RPC_X_BYTE_COUNT_TOO_SMALL,
  7523. RPC_NT_BAD_STUB_DATA, RPC_X_BAD_STUB_DATA,
  7524. RPC_NT_INVALID_OBJECT, RPC_S_INVALID_OBJECT,
  7525. RPC_NT_GROUP_MEMBER_NOT_FOUND, RPC_S_GROUP_MEMBER_NOT_FOUND,
  7526. RPC_NT_NO_INTERFACES, RPC_S_NO_INTERFACES,
  7527. RPC_NT_CALL_CANCELLED, RPC_S_CALL_CANCELLED,
  7528. RPC_NT_BINDING_INCOMPLETE, RPC_S_BINDING_INCOMPLETE,
  7529. RPC_NT_COMM_FAILURE, RPC_S_COMM_FAILURE,
  7530. RPC_NT_UNSUPPORTED_AUTHN_LEVEL, RPC_S_UNSUPPORTED_AUTHN_LEVEL,
  7531. RPC_NT_NO_PRINC_NAME, RPC_S_NO_PRINC_NAME,
  7532. RPC_NT_NOT_RPC_ERROR, RPC_S_NOT_RPC_ERROR,
  7533. RPC_NT_UUID_LOCAL_ONLY, RPC_S_UUID_LOCAL_ONLY,
  7534. RPC_NT_SEC_PKG_ERROR, RPC_S_SEC_PKG_ERROR,
  7535. RPC_NT_NOT_CANCELLED, RPC_S_NOT_CANCELLED,
  7536. RPC_NT_INVALID_ES_ACTION, RPC_X_INVALID_ES_ACTION,
  7537. RPC_NT_WRONG_ES_VERSION, RPC_X_WRONG_ES_VERSION,
  7538. RPC_NT_WRONG_STUB_VERSION, RPC_X_WRONG_STUB_VERSION,
  7539. RPC_NT_INVALID_PIPE_OBJECT, RPC_X_INVALID_PIPE_OBJECT,
  7540. RPC_NT_WRONG_PIPE_VERSION, RPC_X_WRONG_PIPE_VERSION,
  7541. RPC_NT_SEND_INCOMPLETE, RPC_S_SEND_INCOMPLETE,
  7542. RPC_NT_INVALID_ASYNC_HANDLE, RPC_S_INVALID_ASYNC_HANDLE,
  7543. RPC_NT_INVALID_ASYNC_CALL, RPC_S_INVALID_ASYNC_CALL,
  7544. RPC_NT_PIPE_CLOSED, RPC_X_PIPE_CLOSED,
  7545. RPC_NT_PIPE_EMPTY, RPC_X_PIPE_EMPTY,
  7546. RPC_NT_PIPE_DISCIPLINE_ERROR, RPC_X_PIPE_DISCIPLINE_ERROR,
  7547. // Terminal Server Specific Status.
  7548. STATUS_CTX_CLOSE_PENDING, ERROR_CTX_CLOSE_PENDING,
  7549. STATUS_CTX_NO_OUTBUF, ERROR_CTX_NO_OUTBUF,
  7550. STATUS_CTX_MODEM_INF_NOT_FOUND, ERROR_CTX_MODEM_INF_NOT_FOUND,
  7551. STATUS_CTX_INVALID_MODEMNAME, ERROR_CTX_INVALID_MODEMNAME,
  7552. STATUS_CTX_RESPONSE_ERROR, ERROR_CTX_MODEM_RESPONSE_ERROR,
  7553. STATUS_CTX_MODEM_RESPONSE_TIMEOUT, ERROR_CTX_MODEM_RESPONSE_TIMEOUT,
  7554. STATUS_CTX_MODEM_RESPONSE_NO_CARRIER, ERROR_CTX_MODEM_RESPONSE_NO_CARRIER,
  7555. STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE, ERROR_CTX_MODEM_RESPONSE_NO_DIALTONE,
  7556. STATUS_CTX_MODEM_RESPONSE_BUSY, ERROR_CTX_MODEM_RESPONSE_BUSY,
  7557. STATUS_CTX_MODEM_RESPONSE_VOICE, ERROR_CTX_MODEM_RESPONSE_VOICE,
  7558. STATUS_CTX_TD_ERROR, ERROR_CTX_TD_ERROR,
  7559. STATUS_LPC_REPLY_LOST, ERROR_CONNECTION_ABORTED,
  7560. STATUS_CTX_WINSTATION_NAME_INVALID, ERROR_CTX_WINSTATION_NAME_INVALID,
  7561. STATUS_CTX_WINSTATION_NOT_FOUND, ERROR_CTX_WINSTATION_NOT_FOUND,
  7562. STATUS_CTX_WINSTATION_NAME_COLLISION, ERROR_CTX_WINSTATION_ALREADY_EXISTS,
  7563. STATUS_CTX_WINSTATION_BUSY, ERROR_CTX_WINSTATION_BUSY,
  7564. STATUS_CTX_GRAPHICS_INVALID, ERROR_CTX_GRAPHICS_INVALID,
  7565. STATUS_CTX_BAD_VIDEO_MODE, ERROR_CTX_BAD_VIDEO_MODE,
  7566. STATUS_CTX_NOT_CONSOLE, ERROR_CTX_NOT_CONSOLE,
  7567. STATUS_CTX_CLIENT_QUERY_TIMEOUT, ERROR_CTX_CLIENT_QUERY_TIMEOUT,
  7568. STATUS_CTX_CONSOLE_DISCONNECT, ERROR_CTX_CONSOLE_DISCONNECT,
  7569. STATUS_CTX_CONSOLE_CONNECT, ERROR_CTX_CONSOLE_CONNECT,
  7570. STATUS_CTX_SHADOW_DENIED, ERROR_CTX_SHADOW_DENIED,
  7571. STATUS_CTX_SHADOW_INVALID, ERROR_CTX_SHADOW_INVALID,
  7572. STATUS_CTX_SHADOW_DISABLED, ERROR_CTX_SHADOW_DISABLED,
  7573. STATUS_CTX_WINSTATION_ACCESS_DENIED, ERROR_CTX_WINSTATION_ACCESS_DENIED,
  7574. STATUS_CTX_INVALID_PD, ERROR_CTX_INVALID_PD,
  7575. STATUS_CTX_PD_NOT_FOUND, ERROR_CTX_PD_NOT_FOUND,
  7576. STATUS_CTX_INVALID_WD, ERROR_CTX_INVALID_WD,
  7577. STATUS_CTX_WD_NOT_FOUND, ERROR_CTX_WD_NOT_FOUND,
  7578. STATUS_CTX_CLIENT_LICENSE_IN_USE, ERROR_CTX_CLIENT_LICENSE_IN_USE,
  7579. STATUS_CTX_CLIENT_LICENSE_NOT_SET, ERROR_CTX_CLIENT_LICENSE_NOT_SET,
  7580. STATUS_CTX_LICENSE_NOT_AVAILABLE, ERROR_CTX_LICENSE_NOT_AVAILABLE,
  7581. STATUS_CTX_LICENSE_CLIENT_INVALID, ERROR_CTX_LICENSE_CLIENT_INVALID,
  7582. STATUS_CTX_LICENSE_EXPIRED, ERROR_CTX_LICENSE_EXPIRED,
  7583. };
  7584. /*
  7585. * WinStationWinerrorToNtStatus
  7586. * Translate a Windows error code into an NTSTATUS code.
  7587. */
  7588. NTSTATUS
  7589. WinStationWinerrorToNtStatus(ULONG ulWinError)
  7590. {
  7591. ULONG ulIndex;
  7592. for (ulIndex = 0 ; ulIndex < sizeof(CodePairs)/sizeof(CodePairs[0]) ; ulIndex+=2) {
  7593. if (CodePairs[ ulIndex+1 ] == ulWinError ) {
  7594. return (NTSTATUS) CodePairs[ ulIndex];
  7595. }
  7596. }
  7597. return STATUS_UNSUCCESSFUL;
  7598. }
  7599. /*
  7600. * WinStationSetMaxOustandingConnections() set the default values
  7601. * for the maximum number of outstanding connection connections.
  7602. * Reads the registry configuration for it if it exists.
  7603. */
  7604. VOID
  7605. WinStationSetMaxOustandingConnections()
  7606. {
  7607. SYSTEM_BASIC_INFORMATION BasicInfo;
  7608. HKEY hKey;
  7609. NTSTATUS Status;
  7610. BOOL bLargeMachine = FALSE;
  7611. // Initialize date of last delayed connection that was logged into
  7612. // event log. In order not to flood event log with what may not be a DOS
  7613. // attack but just a normal regulation action, delayed connection are not
  7614. // logged more than once in 24h.
  7615. GetSystemTime(&LastLoggedDelayConnection);
  7616. // Init the default values for maximum outstanding connection and
  7617. // Maximumn outstanding connections from single IP address. For
  7618. // Non server platforms these are fixed values.
  7619. if (!gbServer) {
  7620. MaxOutStandingConnect = MAX_DEFAULT_CONNECTIONS_PRO;
  7621. MaxSingleOutStandingConnect = MAX_DEFAULT_SINGLE_CONNECTIONS_PRO;
  7622. } else {
  7623. // Determine if this Machine has over 512Mb of memory
  7624. // In order to set defaults Values (registry settings overide this anyway).
  7625. // Default value are not changed for machines over 512 Mb : Session regulation
  7626. // is trigered if we have 50 outstanding connection and we will wait 30 seconds
  7627. // before acception new connections. For machines with less than 512 Mb, regulation
  7628. // needs to be stronger : it is trigered at lower number of outstanding connections and we will
  7629. // wait 70 seconds before accepting new connections.
  7630. MaxOutStandingConnect = MAX_DEFAULT_CONNECTIONS;
  7631. Status = NtQuerySystemInformation(
  7632. SystemBasicInformation,
  7633. &BasicInfo,
  7634. sizeof(BasicInfo),
  7635. NULL
  7636. );
  7637. if (NT_SUCCESS(Status)) {
  7638. if (BasicInfo.PageSize > 1024*1024) {
  7639. MaxOutStandingConnect = MAX_DEFAULT_CONNECTIONS;
  7640. DelayConnectionTime = 30*1000;
  7641. }else{
  7642. ULONG ulPagesPerMeg = 1024*1024/BasicInfo.PageSize;
  7643. ULONG ulMemSizeInMegabytes = BasicInfo.NumberOfPhysicalPages/ulPagesPerMeg ;
  7644. if (ulMemSizeInMegabytes >= 512) {
  7645. MaxOutStandingConnect = MAX_DEFAULT_CONNECTIONS;
  7646. DelayConnectionTime = 70*1000;
  7647. } else if (ulMemSizeInMegabytes >= 256) {
  7648. MaxOutStandingConnect = 15;
  7649. DelayConnectionTime = 70*1000;
  7650. } else if (ulMemSizeInMegabytes >= 128) {
  7651. MaxOutStandingConnect = 10;
  7652. DelayConnectionTime = 70*1000;
  7653. } else {
  7654. MaxOutStandingConnect = 5;
  7655. DelayConnectionTime = 70*1000;
  7656. }
  7657. }
  7658. }
  7659. //
  7660. // set max number of outstanding connection from single IP
  7661. //
  7662. if ( MaxOutStandingConnect < MAX_SINGLE_CONNECT_THRESHOLD_DIFF*5)
  7663. {
  7664. MaxSingleOutStandingConnect = MaxOutStandingConnect - 1;
  7665. } else {
  7666. MaxSingleOutStandingConnect = MaxOutStandingConnect - MAX_SINGLE_CONNECT_THRESHOLD_DIFF;
  7667. }
  7668. }
  7669. }
  7670. /*
  7671. * IsClientOnSameMachine() Determines if the client is running on the same
  7672. * machine when this posible (currently implemted only for clients connected
  7673. * through TCP/IP). When we can detect the client is on the same machine, we can
  7674. * earlier fail operations like trying to reconecting the console to that client.
  7675. * This way we can fail the operation quicly without going through annoying
  7676. * protocol timeouts.
  7677. */
  7678. BOOL
  7679. IsClientOnSameMachine(PWINSTATION pWinStation)
  7680. {
  7681. ADDRINFO *AddrInfo, *AI;
  7682. int RetVal;
  7683. struct sockaddr_in *pIPV4addr;
  7684. struct sockaddr_in6 *pIPV6addr;
  7685. PBYTE pServerAddrByte;
  7686. PBYTE pClientAddrByte;
  7687. DWORD dwIP4[4];
  7688. BYTE achIP4[4];
  7689. char achCompterName[256];
  7690. DWORD dwComputerNameSize;
  7691. //Return if WinSock couldn't be initialized
  7692. if (!gbWinSockInitialized) {
  7693. return FALSE;
  7694. }
  7695. // setup the client addrees for comparing with server adresses
  7696. switch (pWinStation->Client.ClientAddressFamily ) {
  7697. // For IPV4 the address in client data is represented as a WCHAR string
  7698. case AF_INET:
  7699. swscanf(pWinStation->Client.ClientAddress, L"%u.%u.%u.%u", &dwIP4[0], &dwIP4[1], &dwIP4[2], &dwIP4[3] );
  7700. achIP4[0] = (BYTE) dwIP4[0];
  7701. achIP4[1] = (BYTE) dwIP4[1];
  7702. achIP4[2] = (BYTE) dwIP4[2];
  7703. achIP4[3] = (BYTE) dwIP4[3];
  7704. pClientAddrByte = &achIP4[0];
  7705. break;
  7706. // For IPv6 the adress in client data is assumed to be in binary form
  7707. case AF_INET6:
  7708. pClientAddrByte = (PBYTE) pWinStation->Client.ClientAddress;
  7709. break;
  7710. default:
  7711. return FALSE;
  7712. }
  7713. // Get the server adresses.
  7714. dwComputerNameSize = sizeof(achCompterName);
  7715. if (!GetComputerNameA(achCompterName,&dwComputerNameSize)) {
  7716. return FALSE;
  7717. }
  7718. RetVal = getaddrinfo(achCompterName, NULL, NULL, &AddrInfo);
  7719. if (RetVal != 0) {
  7720. DBGPRINT (("Cannot resolve address, error: %d\n", RetVal));
  7721. return FALSE;
  7722. } else{
  7723. // Compare all server adresses with client till a match is found.
  7724. for (AI = AddrInfo; AI != NULL; AI = AI->ai_next) {
  7725. if (pWinStation->Client.ClientAddressFamily == (ULONG)AI->ai_family &&
  7726. AI->ai_addrlen <= sizeof(pWinStation->Client.ClientAddress) ) {
  7727. switch (pWinStation->Client.ClientAddressFamily) {
  7728. case AF_INET:
  7729. if (AI->ai_addrlen >= sizeof(struct sockaddr_in)) {
  7730. pIPV4addr = (struct sockaddr_in *) AI->ai_addr;
  7731. pServerAddrByte = (PBYTE)&pIPV4addr->sin_addr;
  7732. if (RtlEqualMemory(pClientAddrByte,pServerAddrByte, 4)) {
  7733. return TRUE;
  7734. }
  7735. }
  7736. break;
  7737. case AF_INET6:
  7738. if (AI->ai_addrlen >= sizeof(struct sockaddr_in6)) {
  7739. pIPV6addr = (struct sockaddr_in6 *) AI->ai_addr;
  7740. pServerAddrByte = (PBYTE)&pIPV6addr->sin6_addr;
  7741. if (RtlEqualMemory(pClientAddrByte,pServerAddrByte, 16)) {
  7742. return TRUE;
  7743. }
  7744. }
  7745. break;
  7746. default:
  7747. break;
  7748. }
  7749. }
  7750. }
  7751. }
  7752. return FALSE;
  7753. #if 0
  7754. char hostname[(512+1)*sizeof(TCHAR)];
  7755. int err;
  7756. int i,j;
  7757. struct hostent* phostent;
  7758. err=gethostname(hostname, sizeof(hostname));
  7759. if (err == 0) {
  7760. ;
  7761. if ( (phostent = gethostbyname(hostname)) !=NULL) {
  7762. switch(phostent->h_addrtype){
  7763. case AF_INET:
  7764. if (pWinStation->Client.ClientAddressFamily == AF_INET) {
  7765. BYTE ipaddress[4];
  7766. swscanf(pWinStation->Client.ClientAddress, L"%u.%u.%u.%u", &ipaddress[0], &ipaddress[1], &ipaddress[2], &ipaddress[3] );
  7767. j=0;
  7768. while (phostent->h_addr_list[j] != NULL) {
  7769. for (i=0; i < 4 ; i++) {
  7770. if (ipaddress[i] != (BYTE) phostent->h_addr_list[j][i]) {
  7771. break;
  7772. }
  7773. if (i == 3 ) {
  7774. return TRUE;
  7775. }
  7776. }
  7777. j++;
  7778. }
  7779. }
  7780. default:
  7781. break;
  7782. }
  7783. }
  7784. }
  7785. return FALSE;
  7786. #endif
  7787. }
  7788. /*
  7789. * Make sure we can Preallocate an Idle session before allowing console disconnect.
  7790. *
  7791. */
  7792. NTSTATUS
  7793. CheckIdleWinstation()
  7794. {
  7795. PWINSTATION pWinStation;
  7796. NTSTATUS Status;
  7797. pWinStation = FindIdleWinStation();
  7798. if ( pWinStation == NULL ) {
  7799. /*
  7800. * Create another idle WinStation
  7801. */
  7802. Status = WinStationCreateWorker( NULL, NULL );
  7803. if ( NT_SUCCESS( Status ) ) {
  7804. pWinStation = FindIdleWinStation();
  7805. if ( pWinStation == NULL ) {
  7806. return STATUS_INSUFFICIENT_RESOURCES;
  7807. }
  7808. } else{
  7809. return STATUS_INSUFFICIENT_RESOURCES;
  7810. }
  7811. }
  7812. ReleaseWinStation(pWinStation);
  7813. return STATUS_SUCCESS;
  7814. }
  7815. NTSTATUS
  7816. InitializeWinStationSecurityLock(
  7817. VOID
  7818. )
  7819. {
  7820. NTSTATUS Status ;
  7821. try
  7822. {
  7823. RtlInitializeResource( &WinStationSecurityLock );
  7824. Status = STATUS_SUCCESS ;
  7825. }
  7826. except (EXCEPTION_EXECUTE_HANDLER)
  7827. {
  7828. Status = GetExceptionCode();
  7829. }
  7830. return Status;
  7831. }
  7832. //gets the product id from the registry
  7833. NTSTATUS
  7834. GetProductIdFromRegistry( WCHAR* DigProductId, DWORD dwSize )
  7835. {
  7836. HKEY hKey = NULL;
  7837. NTSTATUS status = STATUS_UNSUCCESSFUL;
  7838. ZeroMemory( DigProductId, dwSize );
  7839. if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_WINDOWS_KEY, 0, KEY_READ, &hKey) == ERROR_SUCCESS )
  7840. {
  7841. DWORD dwType = REG_SZ;
  7842. if( RegQueryValueEx( hKey,
  7843. L"ProductId", NULL, &dwType,
  7844. (LPBYTE)DigProductId,
  7845. &dwSize
  7846. ) == ERROR_SUCCESS )
  7847. status = STATUS_SUCCESS;
  7848. }
  7849. if (hKey)
  7850. RegCloseKey( hKey );
  7851. return status;
  7852. }
  7853. //
  7854. // Gets the remote IP address of the connections
  7855. // and supports statistics of how many outstanding connections
  7856. // are there for this client, if the number of outstanding connections
  7857. // reaches MaxSingleOutStandingConnections, *pbBlocked is returned FALSE
  7858. // the functions returns TRUE on success
  7859. //
  7860. // Paramters:
  7861. // pContext
  7862. // pEndpoint - handle of this connection
  7863. // EndpointLength - td layer needs the length
  7864. // pin_addr - returns remote IP address
  7865. // pbBlocked - returns TRUE if the connection has to be blocked, because of excessive number of
  7866. // outstanding connections
  7867. //
  7868. BOOL
  7869. Filter_AddOutstandingConnection(
  7870. IN HANDLE pContext,
  7871. IN PVOID pEndpoint,
  7872. IN ULONG EndpointLength,
  7873. OUT PBYTE pin_addr,
  7874. OUT PUINT puAddrSize,
  7875. OUT BOOLEAN *pbBlocked
  7876. )
  7877. {
  7878. BOOL rv = FALSE;
  7879. PTS_OUTSTANDINGCONNECTION pIter, pPrev;
  7880. TS_OUTSTANDINGCONNECTION key;
  7881. struct sockaddr_in6 addr6;
  7882. ULONG AddrBytesReturned;
  7883. NTSTATUS Status;
  7884. PVOID paddr;
  7885. BOOL bLocked = FALSE;
  7886. PVOID bSucc;
  7887. BOOLEAN bNewElement;
  7888. ULONGLONG currentTime;
  7889. *pbBlocked = FALSE;
  7890. Status = IcaStackIoControl( pContext,
  7891. IOCTL_TS_STACK_QUERY_REMOTEADDRESS,
  7892. pEndpoint,
  7893. EndpointLength,
  7894. &addr6,
  7895. sizeof( addr6 ),
  7896. &AddrBytesReturned );
  7897. if ( !NT_SUCCESS( Status ))
  7898. {
  7899. goto exitpt;
  7900. }
  7901. if ( AF_INET == addr6.sin6_family )
  7902. {
  7903. key.uAddrSize = 4;
  7904. paddr = &(((struct sockaddr_in *)&addr6)->sin_addr.s_addr);
  7905. } else if ( AF_INET6 == addr6.sin6_family )
  7906. {
  7907. key.uAddrSize = 16;
  7908. paddr = &(addr6.sin6_addr);
  7909. } else {
  7910. ASSERT( 0 );
  7911. }
  7912. ASSERT ( *puAddrSize >= key.uAddrSize );
  7913. RtlCopyMemory( pin_addr, paddr, key.uAddrSize );
  7914. *puAddrSize = key.uAddrSize;
  7915. ENTERCRIT( &FilterLock );
  7916. bLocked = TRUE;
  7917. //
  7918. // Check first in the outstanding connections
  7919. //
  7920. RtlCopyMemory( key.addr, paddr, key.uAddrSize );
  7921. pIter = RtlLookupElementGenericTable( &gOutStandingConnections, &key );
  7922. if ( NULL == pIter )
  7923. {
  7924. //
  7925. // check in the blocked connections list
  7926. //
  7927. pPrev = NULL;
  7928. pIter = g_pBlockedConnections;
  7929. while ( NULL != pIter )
  7930. {
  7931. if ( key.uAddrSize == pIter->uAddrSize &&
  7932. key.uAddrSize == RtlCompareMemory( pIter->addr, paddr, key.uAddrSize ))
  7933. {
  7934. break;
  7935. }
  7936. pPrev = pIter;
  7937. pIter = pIter->pNext;
  7938. }
  7939. if ( NULL != pIter )
  7940. {
  7941. pIter->NumOutStandingConnect ++;
  7942. //
  7943. // already blocked, check for exparation time
  7944. //
  7945. GetSystemTimeAsFileTime( (LPFILETIME)&currentTime );
  7946. if ( currentTime > pIter->blockUntilTime )
  7947. {
  7948. //
  7949. // unblock, remove from list
  7950. //
  7951. pIter->blockUntilTime = 0;
  7952. if ( NULL != pPrev )
  7953. {
  7954. pPrev->pNext = pIter->pNext;
  7955. } else {
  7956. g_pBlockedConnections = pIter->pNext;
  7957. }
  7958. bSucc = RtlInsertElementGenericTable( &gOutStandingConnections, pIter, sizeof( *pIter ), &bNewElement );
  7959. if ( !bSucc )
  7960. {
  7961. MemFree( pIter );
  7962. goto exitpt;
  7963. }
  7964. ASSERT( bNewElement );
  7965. MemFree( pIter );
  7966. } else {
  7967. *pbBlocked = TRUE;
  7968. }
  7969. } else {
  7970. //
  7971. // this will be a new connection
  7972. //
  7973. key.NumOutStandingConnect = 1;
  7974. bSucc = RtlInsertElementGenericTable( &gOutStandingConnections, &key, sizeof( key ), &bNewElement );
  7975. if ( !bSucc )
  7976. {
  7977. goto exitpt;
  7978. }
  7979. ASSERT( bNewElement );
  7980. }
  7981. } else {
  7982. pIter->NumOutStandingConnect ++;
  7983. //
  7984. // Check if we need to block this connection
  7985. //
  7986. if ( pIter->NumOutStandingConnect > MaxSingleOutStandingConnect )
  7987. {
  7988. *pbBlocked = TRUE;
  7989. key.NumOutStandingConnect = pIter->NumOutStandingConnect;
  7990. GetSystemTimeAsFileTime( (LPFILETIME)&currentTime );
  7991. // DelayConnectionTime is in ms
  7992. // currentTime is in 100s ns
  7993. key.blockUntilTime = currentTime + ((ULONGLONG)10000) * ((ULONGLONG)DelayConnectionTime);
  7994. RtlDeleteElementGenericTable( &gOutStandingConnections, &key );
  7995. //
  7996. // add to the blocked connections
  7997. //
  7998. pIter = MemAlloc( sizeof( *pIter ));
  7999. if ( NULL == pIter )
  8000. {
  8001. goto exitpt;
  8002. }
  8003. RtlCopyMemory( pIter, &key, sizeof( *pIter ));
  8004. pIter->pNext = g_pBlockedConnections;
  8005. g_pBlockedConnections = pIter;
  8006. //
  8007. // log at most one event on every 15 minutes
  8008. //
  8009. if ( LastLoggedBlockedConnection + ((ULONGLONG)10000) * (15 * 60 * 1000) < currentTime )
  8010. {
  8011. LastLoggedBlockedConnection = currentTime;
  8012. WriteErrorLogEntry( EVENT_TOO_MANY_CONNECTIONS, &key.addr, key.uAddrSize );
  8013. }
  8014. }
  8015. }
  8016. rv = TRUE;
  8017. exitpt:
  8018. if ( bLocked )
  8019. {
  8020. LEAVECRIT( &FilterLock );
  8021. }
  8022. return rv;
  8023. }
  8024. //
  8025. // Removes outstanding connections added in AddOutStandingConnection
  8026. //
  8027. BOOL
  8028. Filter_RemoveOutstandingConnection(
  8029. IN PBYTE paddr,
  8030. IN UINT uAddrSize
  8031. )
  8032. {
  8033. PTS_OUTSTANDINGCONNECTION pIter, pPrev, pNext;
  8034. TS_OUTSTANDINGCONNECTION key;
  8035. ULONGLONG currentTime;
  8036. NTSTATUS Status;
  8037. ULONG AddrBytesReturned;
  8038. #if DBG
  8039. BOOL bFound = FALSE;
  8040. #endif
  8041. pPrev = NULL;
  8042. GetSystemTimeAsFileTime( (LPFILETIME)&currentTime );
  8043. key.uAddrSize = uAddrSize;
  8044. RtlCopyMemory( key.addr, paddr, uAddrSize );
  8045. ENTERCRIT( &FilterLock );
  8046. pIter = RtlLookupElementGenericTable( &gOutStandingConnections, &key );
  8047. if ( NULL != pIter )
  8048. {
  8049. #if DBG
  8050. bFound = TRUE;
  8051. #endif
  8052. pIter->NumOutStandingConnect--;
  8053. //
  8054. // cleanup connections w/o reference
  8055. //
  8056. if ( 0 == pIter->NumOutStandingConnect )
  8057. {
  8058. RtlDeleteElementGenericTable( &gOutStandingConnections, &key );
  8059. }
  8060. }
  8061. //
  8062. // work through the blocked list
  8063. //
  8064. pIter = g_pBlockedConnections;
  8065. while( pIter )
  8066. {
  8067. if ( uAddrSize == pIter->uAddrSize &&
  8068. uAddrSize == RtlCompareMemory( pIter->addr, paddr, uAddrSize ))
  8069. {
  8070. ASSERT( 0 != pIter->NumOutStandingConnect );
  8071. pIter->NumOutStandingConnect--;
  8072. #if DBG
  8073. ASSERT( !bFound );
  8074. bFound = TRUE;
  8075. #endif
  8076. }
  8077. //
  8078. // cleanup all connections w/o references
  8079. //
  8080. if ( 0 == pIter->NumOutStandingConnect &&
  8081. currentTime > pIter->blockUntilTime )
  8082. {
  8083. if ( NULL == pPrev )
  8084. {
  8085. g_pBlockedConnections = pIter->pNext;
  8086. } else {
  8087. pPrev->pNext = pIter->pNext;
  8088. }
  8089. //
  8090. // remove item and advance to the next
  8091. //
  8092. pNext = pIter->pNext;
  8093. MemFree( pIter );
  8094. pIter = pNext;
  8095. } else {
  8096. //
  8097. // advance to the next item
  8098. //
  8099. pPrev = pIter;
  8100. pIter = pIter->pNext;
  8101. }
  8102. }
  8103. ASSERT( bFound );
  8104. /*
  8105. * Decrement the number of outstanding connections.
  8106. * If connections drop back to max value, set the connect event.
  8107. */
  8108. #if DBG
  8109. //
  8110. // ensure proper cleanup
  8111. //
  8112. bFound = ( 0 == gOutStandingConnections.NumberGenericTableElements );
  8113. for( pIter = g_pBlockedConnections; pIter; pIter = pIter->pNext )
  8114. {
  8115. bFound = bFound & ( 0 == pIter->NumOutStandingConnect );
  8116. }
  8117. #endif
  8118. LEAVECRIT( &FilterLock );
  8119. return TRUE;
  8120. }
  8121. /*****************************************************************************
  8122. *
  8123. * Filter_CompareConnectionEntry
  8124. *
  8125. * Generic table support.Compare two connection entries
  8126. *
  8127. *
  8128. ****************************************************************************/
  8129. RTL_GENERIC_COMPARE_RESULTS
  8130. NTAPI
  8131. Filter_CompareConnectionEntry(
  8132. IN struct _RTL_GENERIC_TABLE *Table,
  8133. IN PVOID FirstInstance,
  8134. IN PVOID SecondInstance
  8135. )
  8136. {
  8137. PTS_OUTSTANDINGCONNECTION pFirst, pSecond;
  8138. INT rc;
  8139. pFirst = (PTS_OUTSTANDINGCONNECTION)FirstInstance;
  8140. pSecond = (PTS_OUTSTANDINGCONNECTION)SecondInstance;
  8141. if ( pFirst->uAddrSize < pSecond->uAddrSize )
  8142. {
  8143. return GenericLessThan;
  8144. } else if ( pFirst->uAddrSize > pSecond->uAddrSize )
  8145. {
  8146. return GenericGreaterThan;
  8147. }
  8148. rc = memcmp( pFirst->addr, pSecond->addr, pFirst->uAddrSize );
  8149. return ( rc < 0 )?GenericLessThan:
  8150. ( rc > 0 )?GenericGreaterThan:
  8151. GenericEqual;
  8152. }
  8153. /*****************************************************************************
  8154. *
  8155. * Filter_AllocateConnectionEntry
  8156. *
  8157. * Generic table support. Allocates a new table entry
  8158. *
  8159. *
  8160. ****************************************************************************/
  8161. PVOID
  8162. Filter_AllocateConnectionEntry(
  8163. IN struct _RTL_GENERIC_TABLE *Table,
  8164. IN CLONG ByteSize
  8165. )
  8166. {
  8167. return MemAlloc( ByteSize );
  8168. }
  8169. /*****************************************************************************
  8170. *
  8171. * Filter_FreeConnectionEntry
  8172. *
  8173. * Generic table support. frees a new table entry
  8174. *
  8175. *
  8176. ****************************************************************************/
  8177. VOID
  8178. Filter_FreeConnectionEntry (
  8179. IN struct _RTL_GENERIC_TABLE *Table,
  8180. IN PVOID Buffer
  8181. )
  8182. {
  8183. MemFree( Buffer );
  8184. }
  8185. VOID
  8186. Filter_DestroyList(
  8187. VOID
  8188. )
  8189. {
  8190. PTS_OUTSTANDINGCONNECTION p;
  8191. TS_OUTSTANDINGCONNECTION con;
  8192. while ( NULL != g_pBlockedConnections )
  8193. {
  8194. p = g_pBlockedConnections->pNext;
  8195. MemFree( g_pBlockedConnections );
  8196. g_pBlockedConnections = p;
  8197. }
  8198. while (p = RtlEnumerateGenericTable( &gOutStandingConnections, TRUE))
  8199. {
  8200. RtlCopyMemory( &con, p, sizeof( con ));
  8201. RtlDeleteElementGenericTable( &gOutStandingConnections, &con);
  8202. }
  8203. }
  8204. //
  8205. // ComputeHMACVerifier
  8206. // Compute the HMAC verifier from the random
  8207. // and the cookie
  8208. //
  8209. BOOL
  8210. ComputeHMACVerifier(
  8211. PBYTE pCookie, //IN - the shared secret
  8212. LONG cbCookieLen, //IN - the shared secret len
  8213. PBYTE pRandom, //IN - the session random
  8214. LONG cbRandomLen, //IN - the session random len
  8215. PBYTE pVerifier, //OUT- the verifier
  8216. LONG cbVerifierLen //IN - the verifier buffer length
  8217. )
  8218. {
  8219. HMACMD5_CTX hmacctx;
  8220. BOOL fRet = FALSE;
  8221. ASSERT(cbVerifierLen >= MD5DIGESTLEN);
  8222. if (!(pCookie &&
  8223. cbCookieLen &&
  8224. pRandom &&
  8225. cbRandomLen &&
  8226. pVerifier &&
  8227. cbVerifierLen)) {
  8228. goto bail_out;
  8229. }
  8230. HMACMD5Init(&hmacctx, pCookie, cbCookieLen);
  8231. HMACMD5Update(&hmacctx, pRandom, cbRandomLen);
  8232. HMACMD5Final(&hmacctx, pVerifier);
  8233. fRet = TRUE;
  8234. bail_out:
  8235. return fRet;
  8236. }
  8237. //
  8238. // Extract the session to reconnect to from the ARC info
  8239. // also do the necessary security checks
  8240. //
  8241. // Params:
  8242. // pClientArcInfo - autoreconnect information from the client
  8243. //
  8244. // Returns:
  8245. // If all security checks pass and pArc is valid then winstation
  8246. // to reconnect to is returned. Else NULL
  8247. //
  8248. // NOTE: WinStation returned is left LOCKED.
  8249. //
  8250. PWINSTATION
  8251. GetWinStationFromArcInfo(
  8252. PBYTE pClientRandom,
  8253. LONG cbClientRandomLen,
  8254. PTS_AUTORECONNECTINFO pClientArcInfo
  8255. )
  8256. {
  8257. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  8258. PWINSTATION pWinStation = NULL;
  8259. PWINSTATION pFoundWinStation = NULL;
  8260. ARC_CS_PRIVATE_PACKET UNALIGNED* pCSArcInfo = NULL;
  8261. BYTE arcSCclientBlob[ARC_SC_SECURITY_TOKEN_LEN];
  8262. BYTE hmacVerifier[ARC_CS_SECURITY_TOKEN_LEN];
  8263. PBYTE pServerArcBits = NULL;
  8264. ULONG BytesGot = 0;
  8265. TS_AUTORECONNECTINFO SCAutoReconnectInfo;
  8266. TRACE((hTrace,TC_ICASRV,TT_API1,
  8267. "TERMSRV: WinStation GetWinStationFromArcInfo pRandom:%p len:%d\n",
  8268. pClientRandom, cbClientRandomLen));
  8269. if (!pClientArcInfo) {
  8270. goto error;
  8271. }
  8272. pCSArcInfo = (ARC_CS_PRIVATE_PACKET UNALIGNED*)pClientArcInfo->AutoReconnectInfo;
  8273. if (!pCSArcInfo->cbLen ||
  8274. pCSArcInfo->cbLen < sizeof(ARC_CS_PRIVATE_PACKET)) {
  8275. TRACE((hTrace,TC_ICASRV,TT_ERROR,
  8276. "TERMSRV: GetWinStationFromArcInfo ARC length invalid bailing out\n"));
  8277. goto error;
  8278. }
  8279. memset(arcSCclientBlob, 0, sizeof(arcSCclientBlob));
  8280. pWinStation = FindWinStationById(pCSArcInfo->LogonId, FALSE);
  8281. if (pWinStation) {
  8282. TRACE((hTrace,TC_ICASRV,TT_API1,
  8283. "TERMSRV: GetWinStationFromArcInfo found arc winstation: %d\n",
  8284. pCSArcInfo->LogonId));
  8285. //
  8286. // Do security checks to ensure this is the same winstation
  8287. // that was connected to the client
  8288. //
  8289. //
  8290. // First obtain the last autoreconnect blob sent to the client
  8291. // since we do an inline cookie update in rdpwd
  8292. //
  8293. if (pWinStation->AutoReconnectInfo.Valid) {
  8294. pServerArcBits = pWinStation->AutoReconnectInfo.ArcRandomBits;
  8295. Status = STATUS_SUCCESS;
  8296. }
  8297. else {
  8298. if (pWinStation->pWsx &&
  8299. pWinStation->pWsx->pWsxEscape) {
  8300. if (pWinStation->Terminating ||
  8301. pWinStation->StateFlags & WSF_ST_WINSTATIONTERMINATE ||
  8302. !pWinStation->WinStationName[0]) {
  8303. TRACE((hTrace,TC_ICASRV,TT_ERROR,
  8304. "GetWinStationFromArcInfo skipping escape"
  8305. "to closed stack disconnected %d\n",
  8306. LogonId));
  8307. Status = STATUS_ACCESS_DENIED;
  8308. goto error;
  8309. }
  8310. Status = pWinStation->pWsx->pWsxEscape(
  8311. pWinStation->pWsxContext,
  8312. GET_SC_AUTORECONNECT_INFO,
  8313. NULL,
  8314. 0,
  8315. &SCAutoReconnectInfo,
  8316. sizeof(SCAutoReconnectInfo),
  8317. &BytesGot);
  8318. if (NT_SUCCESS(Status)) {
  8319. ASSERT(SCAutoReconnectInfo.cbAutoReconnectInfo ==
  8320. ARC_SC_SECURITY_TOKEN_LEN);
  8321. }
  8322. pServerArcBits = SCAutoReconnectInfo.AutoReconnectInfo;
  8323. }
  8324. }
  8325. }
  8326. else {
  8327. Status = STATUS_ACCESS_DENIED;
  8328. }
  8329. if (NT_SUCCESS(Status)) {
  8330. //
  8331. // Ensure we got the correct length for the server->client
  8332. // data
  8333. //
  8334. ASSERT(pServerArcBits);
  8335. //
  8336. // Get random
  8337. //
  8338. if (ComputeHMACVerifier(pServerArcBits,
  8339. ARC_SC_SECURITY_TOKEN_LEN,
  8340. pClientRandom,
  8341. cbClientRandomLen,
  8342. (PBYTE)hmacVerifier,
  8343. sizeof(hmacVerifier))) {
  8344. //
  8345. // Check that the verifier matches that sent by the client
  8346. //
  8347. if (!memcmp(hmacVerifier,
  8348. pCSArcInfo->SecurityVerifier,
  8349. sizeof(pCSArcInfo->SecurityVerifier))) {
  8350. TRACE((hTrace,TC_ICASRV,TT_API1,
  8351. "TERMSRV: WinStation ARC info matches - will autoreconnect\n"));
  8352. }
  8353. else {
  8354. TRACE((hTrace,TC_ICASRV,TT_ERROR,
  8355. "TERMSRV: autoreconnect verifier does not match targid:%d!!!\n",
  8356. pWinStation->LogonId));
  8357. //
  8358. // Reset the autoreconnect info
  8359. //
  8360. pWinStation->AutoReconnectInfo.Valid = FALSE;
  8361. memset(pWinStation->AutoReconnectInfo.ArcRandomBits, 0,
  8362. sizeof(pWinStation->AutoReconnectInfo.ArcRandomBits));
  8363. //
  8364. // Mark that no winstation target was found
  8365. //
  8366. goto error;
  8367. }
  8368. }
  8369. pFoundWinStation = pWinStation;
  8370. }
  8371. error:
  8372. if ((NULL == pFoundWinStation) && pWinStation) {
  8373. ReleaseWinStation(pWinStation);
  8374. pWinStation = NULL;
  8375. }
  8376. return pFoundWinStation;
  8377. }
  8378. //
  8379. // Extract the session to reconnect to from the ARC info
  8380. // also do the necessary security checks
  8381. //
  8382. // Params:
  8383. // pWinStation - winstation to reset autoreconnect info for
  8384. //
  8385. VOID
  8386. ResetAutoReconnectInfo( PWINSTATION pWinStation)
  8387. {
  8388. pWinStation->AutoReconnectInfo.Valid = FALSE;
  8389. memset(pWinStation->AutoReconnectInfo.ArcRandomBits, 0,
  8390. sizeof(pWinStation->AutoReconnectInfo.ArcRandomBits));
  8391. }