Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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