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

1230 lines
46 KiB

  1. /****************************************************************************/
  2. // winget.c
  3. //
  4. // TermSrv RPC query handler.
  5. //
  6. // Copyright (C) 1997-2000 Microsoft Corporation
  7. /****************************************************************************/
  8. #include "precomp.h"
  9. #pragma hdrstop
  10. #include "rpcwire.h"
  11. #include "conntfy.h" // for GetLockedState
  12. #include <winsock2.h>
  13. #include <ws2tcpip.h>
  14. #define MODULE_SIZE 1024
  15. extern WCHAR g_DigProductId[CLIENT_PRODUCT_ID_LENGTH];
  16. // Extern function
  17. extern NTSTATUS _CheckCallerLocalAndSystem(VOID);
  18. /*=============================================================================
  19. == Private functions
  20. =============================================================================*/
  21. NTSTATUS xxxGetUserToken(PWINSTATION, WINSTATIONUSERTOKEN UNALIGNED *, ULONG);
  22. /*=============================================================================
  23. == Functions Used
  24. =============================================================================*/
  25. NTSTATUS xxxWinStationQueryInformation(ULONG, WINSTATIONINFOCLASS,
  26. PVOID, ULONG, PULONG);
  27. NTSTATUS
  28. RpcCheckClientAccess(
  29. PWINSTATION pWinStation,
  30. ACCESS_MASK DesiredAccess,
  31. BOOLEAN AlreadyImpersonating
  32. );
  33. NTSTATUS
  34. RpcCheckSystemClientEx(
  35. PWINSTATION pWinStation
  36. );
  37. NTSTATUS
  38. RpcCheckSystemClientNoLogonId(
  39. PWINSTATION pWinStation
  40. );
  41. BOOLEAN
  42. ValidWireBuffer(WINSTATIONINFOCLASS InfoClass,
  43. PVOID WireBuf,
  44. ULONG WireBufLen);
  45. BOOLEAN
  46. IsCallerAllowedPasswordAccess(VOID);
  47. //
  48. // Query client's IP Address.
  49. //
  50. extern NTSTATUS
  51. xxxQueryRemoteAddress(
  52. PWINSTATION pWinStation,
  53. PWINSTATIONREMOTEADDRESS pRemoteAddress
  54. )
  55. {
  56. struct sockaddr_in6 addr6;
  57. ULONG AddrBytesReturned;
  58. NTSTATUS Status;
  59. if( pWinStation->State != State_Active && pWinStation->State != State_Connected )
  60. {
  61. Status = STATUS_CTX_WINSTATION_NOT_FOUND;
  62. }
  63. else
  64. {
  65. Status = IcaStackIoControl( pWinStation->hStack,
  66. IOCTL_TS_STACK_QUERY_REMOTEADDRESS,
  67. pWinStation->pEndpoint,
  68. pWinStation->EndpointLength,
  69. &addr6,
  70. sizeof( addr6 ),
  71. &AddrBytesReturned
  72. );
  73. if( NT_SUCCESS(Status) )
  74. {
  75. pRemoteAddress->sin_family = addr6.sin6_family;
  76. if( AF_INET == addr6.sin6_family )
  77. {
  78. struct sockaddr_in* pAddr = (struct sockaddr_in *)&addr6;
  79. pRemoteAddress->ipv4.sin_port = pAddr->sin_port;
  80. pRemoteAddress->ipv4.in_addr = pAddr->sin_addr.s_addr;
  81. }
  82. else
  83. {
  84. // Support of IPV6 is for next release.
  85. Status = STATUS_NOT_SUPPORTED;
  86. }
  87. }
  88. }
  89. return Status;
  90. }
  91. ULONG GetLoadMetrics(PWINSTATIONLOADINDICATORDATA pLIData)
  92. {
  93. SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo;
  94. SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION ProcessorInfo[MAX_PROCESSORS];
  95. SYSTEM_BASIC_INFORMATION BasicInfo;
  96. LARGE_INTEGER TotalCPU = {0, 0};
  97. LARGE_INTEGER IdleCPU = {0, 0};
  98. LARGE_INTEGER TotalCPUDelta = {0, 0};
  99. LARGE_INTEGER IdleCPUDelta = {0, 0};
  100. ULONG AvgIdleCPU, AvgBusyCPU, CPUConstrainedSessions;
  101. ULONG RemainingSessions = 0;
  102. LOADFACTORTYPE LoadFactor = ErrorConstraint;
  103. ULONG MinSessions;
  104. ULONG NumWinStations;
  105. NTSTATUS StatusPerf, StatusProc, StatusBasic;
  106. ULONG i;
  107. // Initialize additional data area
  108. memset(pLIData->reserved, 0, sizeof(pLIData->reserved));
  109. // Determine the number of active winstations in the system. If there
  110. // aren't any, just assume 1 so we don't have to special case the logic
  111. // too much. Note that this code counts the console.
  112. if (WinStationTotalCount > IdleWinStationPoolCount)
  113. NumWinStations = WinStationTotalCount - IdleWinStationPoolCount;
  114. else
  115. NumWinStations = 1;
  116. TRACE((hTrace, TC_LOAD, TT_API1,
  117. "Session Statistics: Total [%ld], Idle [%ld], Disc [%ld]\n",
  118. WinStationTotalCount, IdleWinStationPoolCount, WinStationDiscCount));
  119. //
  120. // Get basic info like total memory, etc.
  121. //
  122. StatusBasic = NtQuerySystemInformation(SystemBasicInformation,
  123. &BasicInfo, sizeof(BasicInfo),
  124. NULL);
  125. //
  126. // Get resource (memory) utilization metrics
  127. //
  128. StatusPerf = NtQuerySystemInformation(SystemPerformanceInformation,
  129. &SysPerfInfo, sizeof(SysPerfInfo),
  130. NULL);
  131. //
  132. // Get CPU utilization metrics
  133. //
  134. StatusProc = NtQuerySystemInformation(SystemProcessorPerformanceInformation,
  135. ProcessorInfo,
  136. sizeof(ProcessorInfo),
  137. NULL);
  138. if (gLB.fInitialized &&
  139. NT_SUCCESS(StatusPerf) &&
  140. NT_SUCCESS(StatusProc) &&
  141. NT_SUCCESS(StatusBasic)) {
  142. ULONG DefaultPagedPool, DefaultPtes, DefaultCommit;
  143. ULONG CommitAvailable;
  144. //
  145. // Determine resource usage for all sessions, subtracting out the
  146. // resources required by the base system. Readjust the base
  147. // calculations if they become nonsensical.
  148. //
  149. // total committment and average consumption
  150. CommitAvailable = SysPerfInfo.CommitLimit - SysPerfInfo.CommittedPages;
  151. if (gLB.BaselineCommit < SysPerfInfo.CommittedPages) {
  152. gLB.CommitUsed = SysPerfInfo.CommittedPages - gLB.BaselineCommit;
  153. gLB.AvgCommitPerUser = max(gLB.CommitUsed / NumWinStations,
  154. gLB.MinCommitPerUser);
  155. DefaultCommit = FALSE;
  156. }
  157. else {
  158. gLB.CommitUsed = 0;
  159. gLB.AvgCommitPerUser = gLB.MinCommitPerUser;
  160. gLB.BaselineCommit = SysPerfInfo.CommittedPages;
  161. DefaultCommit = TRUE;
  162. }
  163. TRACE((hTrace, TC_LOAD, TT_API1,
  164. " Commit: Base [%6ld], Used [%6ld], Avail: [%6ld], AvgPerUser: [%6ld]%s\n",
  165. gLB.BaselineCommit,
  166. gLB.CommitUsed,
  167. CommitAvailable,
  168. gLB.AvgCommitPerUser,
  169. DefaultCommit ? "*" : ""));
  170. // total system PTEs used and average consumption
  171. if (gLB.BaselineFreePtes > SysPerfInfo.FreeSystemPtes) {
  172. gLB.PtesUsed = gLB.BaselineFreePtes - SysPerfInfo.FreeSystemPtes;
  173. gLB.AvgPtesPerUser = max(gLB.PtesUsed / NumWinStations,
  174. gLB.MinPtesPerUser);
  175. DefaultPtes = FALSE;
  176. }
  177. else {
  178. gLB.PtesUsed = 0;
  179. gLB.AvgPtesPerUser = gLB.MinPtesPerUser;
  180. gLB.BaselineFreePtes = SysPerfInfo.FreeSystemPtes;
  181. DefaultPtes = TRUE;
  182. }
  183. TRACE((hTrace, TC_LOAD, TT_API1,
  184. " Ptes: Base [%6ld], Used [%6ld], Avail: [%6ld], AvgPerUser: [%6ld]%s\n",
  185. gLB.BaselineFreePtes,
  186. gLB.PtesUsed,
  187. SysPerfInfo.FreeSystemPtes,
  188. gLB.AvgPtesPerUser,
  189. DefaultPtes ? "*" : ""));
  190. // paged pool used and average consumption
  191. if (gLB.BaselinePagedPool < SysPerfInfo.PagedPoolPages) {
  192. gLB.PagedPoolUsed = SysPerfInfo.PagedPoolPages - gLB.BaselinePagedPool;
  193. gLB.AvgPagedPoolPerUser = max(gLB.PagedPoolUsed / NumWinStations,
  194. gLB.MinPagedPoolPerUser);
  195. DefaultPagedPool = FALSE;
  196. }
  197. else {
  198. gLB.PagedPoolUsed = 0;
  199. gLB.AvgPagedPoolPerUser = gLB.MinPagedPoolPerUser;
  200. gLB.BaselinePagedPool = SysPerfInfo.PagedPoolPages;
  201. DefaultPagedPool = TRUE;
  202. }
  203. TRACE((hTrace, TC_LOAD, TT_API1,
  204. " PagedPool: Base [%6ld], Used [%6ld], Avail: [%6ld], AvgPerUser: [%6ld]%s\n",
  205. gLB.BaselinePagedPool,
  206. gLB.PagedPoolUsed,
  207. SysPerfInfo.AvailablePagedPoolPages,
  208. gLB.AvgPagedPoolPerUser,
  209. DefaultPagedPool ? "*" : ""));
  210. TRACE((hTrace, TC_LOAD, TT_API1,
  211. " Session Raw: Commit [%4ld], Pte [%4ld], Paged [%4ld]\n",
  212. CommitAvailable / gLB.AvgCommitPerUser,
  213. SysPerfInfo.FreeSystemPtes / gLB.AvgPtesPerUser,
  214. SysPerfInfo.AvailablePagedPoolPages / gLB.AvgPagedPoolPerUser));
  215. // Sum up individual CPU usage
  216. for (i = 0; i < gLB.NumProcessors; i++) {
  217. IdleCPU.QuadPart += ProcessorInfo[i].IdleTime.QuadPart;
  218. TotalCPU.QuadPart += ProcessorInfo[i].KernelTime.QuadPart +
  219. ProcessorInfo[i].UserTime.QuadPart;
  220. }
  221. // Determine CPU deltas for this period
  222. IdleCPUDelta.QuadPart = IdleCPU.QuadPart - gLB.IdleCPU.QuadPart;
  223. TotalCPUDelta.QuadPart = TotalCPU.QuadPart - gLB.TotalCPU.QuadPart;
  224. gLB.IdleCPU.QuadPart = IdleCPU.QuadPart;
  225. gLB.TotalCPU.QuadPart = TotalCPU.QuadPart;
  226. // Determine what portion of 255 units we are idle
  227. AvgIdleCPU = (ULONG) (TotalCPUDelta.QuadPart ?
  228. ((IdleCPUDelta.QuadPart << 8) / TotalCPUDelta.QuadPart)
  229. : 0);
  230. //
  231. // Exponential smoothing:
  232. // gLB.AvgIdleCPU = (ULONG) (alpha * gLB.AvgIdleCPU + (1 - alpha) * AvgIdleCPU)
  233. //
  234. // When Alpha = 0.75, the equation simplifies to the following:
  235. //
  236. gLB.AvgIdleCPU = (3 * gLB.AvgIdleCPU + AvgIdleCPU) >> 2 ;
  237. // Based on current smoothed CPU usage, calculate how much a session uses
  238. // on average and extrapolate to max CPU constrained sessions.
  239. AvgBusyCPU = 255 - gLB.AvgIdleCPU;
  240. if ((AvgBusyCPU > 0) && (AvgBusyCPU <= 255))
  241. CPUConstrainedSessions = (NumWinStations << 8) / AvgBusyCPU;
  242. else
  243. CPUConstrainedSessions = 0xFFFFFFFF;
  244. // Now flip it to remaining CPU constrained sessions. We never let this
  245. // number hit zero since it doesn't mean session creation will fail.
  246. if (CPUConstrainedSessions > NumWinStations)
  247. CPUConstrainedSessions -= NumWinStations;
  248. else
  249. CPUConstrainedSessions = 1;
  250. // Bias the averages a bit to account for growth in the existing sessions
  251. gLB.AvgCommitPerUser += (ULONG) (gLB.AvgCommitPerUser >> SimGrowthBias);
  252. gLB.AvgPtesPerUser += (ULONG) (gLB.AvgPtesPerUser >> SimGrowthBias);
  253. gLB.AvgPagedPoolPerUser += (ULONG) (gLB.AvgPagedPoolPerUser >> SimGrowthBias);
  254. TRACE((hTrace, TC_LOAD, TT_API1,
  255. " Session Avg: Commit [%4ld], Pte [%4ld], Paged [%4ld]\n",
  256. CommitAvailable / gLB.AvgCommitPerUser,
  257. SysPerfInfo.FreeSystemPtes / gLB.AvgPtesPerUser,
  258. SysPerfInfo.AvailablePagedPoolPages / gLB.AvgPagedPoolPerUser));
  259. TRACE((hTrace, TC_LOAD, TT_API1,
  260. " CPU Idle: Current [%4ld], Avg [%4ld], Est [%4ld]\n",
  261. (AvgIdleCPU * 100) / 255,
  262. (gLB.AvgIdleCPU * 100) / 255,
  263. CPUConstrainedSessions));
  264. //
  265. // Find the most constrained resource! Failure on any one of these
  266. // items means we will not be likely to start a session.
  267. //
  268. // Commit Constraint (TODO: needs refinement, doesn't consider paging
  269. RemainingSessions = CommitAvailable / gLB.AvgCommitPerUser ;
  270. LoadFactor = AvailablePagesConstraint;
  271. pLIData->reserved[AvailablePagesConstraint] = RemainingSessions;
  272. // Free System PTEs Constraint
  273. MinSessions = SysPerfInfo.FreeSystemPtes / gLB.AvgPtesPerUser;
  274. if (MinSessions < RemainingSessions) {
  275. RemainingSessions = MinSessions;
  276. LoadFactor = SystemPtesConstraint;
  277. }
  278. pLIData->reserved[SystemPtesConstraint] = MinSessions;
  279. // Paged Pool Constraint
  280. MinSessions = SysPerfInfo.AvailablePagedPoolPages / gLB.AvgPagedPoolPerUser;
  281. if (MinSessions < RemainingSessions) {
  282. RemainingSessions = MinSessions;
  283. LoadFactor = PagedPoolConstraint;
  284. }
  285. pLIData->reserved[PagedPoolConstraint] = MinSessions;
  286. gLB.RemainingSessions = RemainingSessions;
  287. //
  288. // Add in constraints that are good indicators of application performance.
  289. // We will likely create a session if these resources are low, but the
  290. // user experience will suffer.
  291. // CPU Contraint
  292. if (CPUConstrainedSessions < RemainingSessions) {
  293. LoadFactor = CPUConstraint;
  294. RemainingSessions = CPUConstrainedSessions;
  295. }
  296. pLIData->reserved[CPUConstraint] = MinSessions;
  297. gLB.EstimatedSessions = RemainingSessions;
  298. TRACE((hTrace, TC_LOAD, TT_API1,
  299. "Remaining Sessions: Raw: [%4ld], Est: [%4ld], Factor = %s, Commit = %ld\n\n",
  300. gLB.RemainingSessions, gLB.EstimatedSessions,
  301. LoadFactor == AvailablePagesConstraint ? "Available Memory" :
  302. (LoadFactor == SystemPtesConstraint ? "SystemPtes" :
  303. (LoadFactor == PagedPoolConstraint ? "PagedPool" :
  304. (LoadFactor == CPUConstraint ? "CPU" :
  305. "Unknown!"))), SysPerfInfo.CommittedPages
  306. ));
  307. //
  308. // Return data to caller
  309. //
  310. pLIData->RemainingSessionCapacity = gLB.EstimatedSessions;
  311. pLIData->RawSessionCapacity = gLB.RemainingSessions;
  312. pLIData->LoadFactor = LoadFactor;
  313. pLIData->TotalSessions = NumWinStations;
  314. pLIData->DisconnectedSessions = WinStationDiscCount;
  315. // Had to split this up for IA64 alignment issues
  316. pLIData->IdleCPU.HighPart = IdleCPUDelta.HighPart;
  317. pLIData->IdleCPU.LowPart = IdleCPUDelta.LowPart;
  318. pLIData->TotalCPU.HighPart = TotalCPUDelta.HighPart;
  319. pLIData->TotalCPU.LowPart = TotalCPUDelta.LowPart;
  320. }
  321. // The load metrics failed to intialize! Set the capacity sky high to still
  322. // allow access to the server.
  323. else {
  324. RemainingSessions = 0xFFFFFFFF;
  325. pLIData->RemainingSessionCapacity = RemainingSessions;
  326. pLIData->RawSessionCapacity = RemainingSessions;
  327. pLIData->LoadFactor = ErrorConstraint;
  328. pLIData->TotalSessions = NumWinStations;
  329. pLIData->DisconnectedSessions = WinStationDiscCount;
  330. // Had to split this up for IA64 alignment issues
  331. pLIData->IdleCPU.HighPart = 0;
  332. pLIData->IdleCPU.LowPart = 99;
  333. pLIData->TotalCPU.HighPart = 0;
  334. pLIData->TotalCPU.LowPart = 100;
  335. TRACE((hTrace, TC_LOAD, TT_ERROR,
  336. "GetLoadMetrics failed: init [%ld], Proc [%lx], Perf [%lx], Basic [%lx]!\n",
  337. gLB.fInitialized, StatusProc, StatusPerf, StatusBasic));
  338. }
  339. return RemainingSessions;
  340. }
  341. /*******************************************************************************
  342. * xxxWinStationQueryInformation
  343. *
  344. * Query window station information (worker routine)
  345. *
  346. * ENTRY:
  347. * LogonId (input)
  348. * Session ID corresponding to the session.
  349. * WinStationInformationClass (input)
  350. * Specifies the type of information to get from the specified window
  351. * station object.
  352. * pWinStationInformation (output)
  353. * A pointer to a buffer that contains information to get for the
  354. * specified window station. The format and contents of the buffer
  355. * depend on the specified information class being set.
  356. * WinStationInformationLength (input)
  357. * Specifies the length in bytes of the window station information
  358. * buffer.
  359. * pReturnLength (output)
  360. * Specifies the amount returned in the buffer
  361. ******************************************************************************/
  362. NTSTATUS xxxWinStationQueryInformation(
  363. ULONG LogonId,
  364. WINSTATIONINFOCLASS WinStationInformationClass,
  365. PVOID pWinStationInformation,
  366. ULONG WinStationInformationLength,
  367. PULONG pReturnLength)
  368. {
  369. NTSTATUS Status = STATUS_SUCCESS;
  370. HINSTANCE hInstance;
  371. PWINSTATION pWinStation = NULL;
  372. ULONG cbReturned;
  373. ICA_STACK_LAST_INPUT_TIME Ica_Stack_Last_Input_Time;
  374. WINSTATION_APIMSG WMsg;
  375. PWINSTATIONVIDEODATA pVideoData;
  376. HANDLE hVirtual;
  377. ULONG i;
  378. *pReturnLength = 0;
  379. TRACE((hTrace,TC_ICASRV,TT_API2,"TERMSRV: WinStationQueryInformation LogonId=%d, Class=%d\n",
  380. LogonId, (ULONG)WinStationInformationClass));
  381. /*
  382. * Find the WinStation
  383. * Return error if not found or currently terminating.
  384. */
  385. pWinStation = FindWinStationById( LogonId, FALSE );
  386. if (pWinStation == NULL)
  387. return STATUS_CTX_WINSTATION_NOT_FOUND;
  388. if (pWinStation->Terminating) {
  389. ReleaseWinStation(pWinStation);
  390. return STATUS_CTX_CLOSE_PENDING;
  391. }
  392. /*
  393. * Verify that client has QUERY access
  394. */
  395. Status = RpcCheckClientAccess(pWinStation, WINSTATION_QUERY, FALSE);
  396. if (!NT_SUCCESS(Status)) {
  397. ReleaseWinStation(pWinStation);
  398. return Status;
  399. }
  400. switch ( WinStationInformationClass ) {
  401. case WinStationLoadIndicator:
  402. {
  403. PWINSTATIONLOADINDICATORDATA pLIData =
  404. (PWINSTATIONLOADINDICATORDATA) pWinStationInformation;
  405. if (WinStationInformationLength >= sizeof(WINSTATIONLOADINDICATORDATA)) {
  406. GetLoadMetrics(pLIData);
  407. *pReturnLength = sizeof(WINSTATIONLOADINDICATORDATA);
  408. }
  409. else {
  410. Status = STATUS_BUFFER_TOO_SMALL;
  411. }
  412. }
  413. break;
  414. case WinStationInformation:
  415. {
  416. if (!ValidWireBuffer(WinStationInformationClass,
  417. pWinStationInformation,
  418. WinStationInformationLength))
  419. {
  420. Status = STATUS_INVALID_USER_BUFFER;
  421. }
  422. else
  423. {
  424. WINSTATIONINFORMATION *pInfo;
  425. PROTOCOLSTATUS *pIca_Stack_Query_Status;
  426. pInfo = MemAlloc( sizeof( WINSTATIONINFORMATION ) ) ;
  427. if ( pInfo )
  428. {
  429. pIca_Stack_Query_Status = MemAlloc( sizeof( PROTOCOLSTATUS ) );
  430. if ( pIca_Stack_Query_Status )
  431. {
  432. TCHAR *szUserName = NULL, *szDomainName = NULL;
  433. DWORD dwUserSize = MAX_PATH, dwDomainSize = MAX_PATH;
  434. SID_NAME_USE TypeOfAccount;
  435. BOOL LookupResult;
  436. memset( pInfo, 0, sizeof( PWINSTATIONINFORMATION ) );
  437. wcscpy( pInfo->WinStationName, pWinStation->WinStationName );
  438. memcpy( pInfo->Domain, pWinStation->Domain, sizeof( pInfo->Domain ) );
  439. memcpy( pInfo->UserName, pWinStation->UserName, sizeof( pInfo->UserName ) );
  440. // Since the Username stored maybe stale, query the Username again
  441. // Intentionally we do not fail if we are not able to allocate szUserName and szDomainName
  442. // This is because we can send the cached credentials in that case
  443. szUserName = MemAlloc(MAX_PATH);
  444. if ( szUserName ) {
  445. szDomainName = MemAlloc(MAX_PATH);
  446. if ( szDomainName ) {
  447. LookupResult = LookupAccountSid(NULL,
  448. pWinStation->pUserSid,
  449. szUserName,
  450. &dwUserSize,
  451. szDomainName,
  452. &dwDomainSize,
  453. &TypeOfAccount);
  454. if (LookupResult) {
  455. // Re-copy and update WINSTATION struct if the Username or Domain has changed
  456. if ( (szUserName) && (lstrcmpi(pWinStation->UserName, szUserName)) ) {
  457. memcpy( pInfo->UserName, szUserName, sizeof(pInfo->UserName) );
  458. memcpy( pWinStation->UserName, szUserName, sizeof(pWinStation->UserName) );
  459. }
  460. if ( (szDomainName) && (lstrcmpi(pWinStation->Domain, szDomainName)) ) {
  461. memcpy( pInfo->Domain, szDomainName, sizeof(pInfo->Domain) );
  462. memcpy( pWinStation->Domain, szDomainName, sizeof(pWinStation->Domain) );
  463. }
  464. }
  465. }
  466. }
  467. if (szUserName != NULL) {
  468. MemFree(szUserName);
  469. }
  470. if (szDomainName != NULL) {
  471. MemFree(szDomainName);
  472. }
  473. pInfo->ConnectState = pWinStation->State;
  474. pInfo->LogonId = pWinStation->LogonId;
  475. pInfo->ConnectTime = pWinStation->ConnectTime;
  476. pInfo->DisconnectTime = pWinStation->DisconnectTime;
  477. pInfo->LogonTime = pWinStation->LogonTime;
  478. if ( pWinStation->hStack && !pWinStation->fOwnsConsoleTerminal ) {
  479. // Check for availability
  480. if ( pWinStation->pWsx &&
  481. pWinStation->pWsx->pWsxIcaStackIoControl ) {
  482. Status = pWinStation->pWsx->pWsxIcaStackIoControl(
  483. pWinStation->pWsxContext,
  484. pWinStation->hIca,
  485. pWinStation->hStack,
  486. IOCTL_ICA_STACK_QUERY_LAST_INPUT_TIME,
  487. NULL,
  488. 0,
  489. &Ica_Stack_Last_Input_Time,
  490. sizeof( Ica_Stack_Last_Input_Time ),
  491. &cbReturned );
  492. if ( !NT_SUCCESS( Status ) )
  493. {
  494. MemFree( pInfo );
  495. MemFree( pIca_Stack_Query_Status );
  496. break;
  497. }
  498. pInfo->LastInputTime = Ica_Stack_Last_Input_Time.LastInputTime;
  499. }
  500. // Check for availability
  501. if ( pWinStation->pWsx &&
  502. pWinStation->pWsx->pWsxIcaStackIoControl ) {
  503. Status = pWinStation->pWsx->pWsxIcaStackIoControl(
  504. pWinStation->pWsxContext,
  505. pWinStation->hIca,
  506. pWinStation->hStack,
  507. IOCTL_ICA_STACK_QUERY_STATUS,
  508. NULL,
  509. 0,
  510. pIca_Stack_Query_Status,
  511. sizeof( PROTOCOLSTATUS ),
  512. &cbReturned );
  513. if ( !NT_SUCCESS( Status ) )
  514. {
  515. MemFree( pInfo );
  516. MemFree( pIca_Stack_Query_Status );
  517. break;
  518. }
  519. pInfo->Status = *pIca_Stack_Query_Status;
  520. }
  521. /*
  522. * The thinwire cache data is down in WIN32
  523. */
  524. if ( pWinStation->pWin32Context ) {
  525. WMsg.ApiNumber = SMWinStationThinwireStats;
  526. Status = SendWinStationCommand( pWinStation, &WMsg, gbServer?5:1 );
  527. if ( Status == STATUS_SUCCESS ) {
  528. pInfo->Status.Cache = WMsg.u.ThinwireStats.Stats;
  529. pWinStation->Cache = WMsg.u.ThinwireStats.Stats;
  530. } else {
  531. pInfo->Status.Cache = pWinStation->Cache;
  532. }
  533. Status = STATUS_SUCCESS; // ignore errors getting TW stats
  534. }
  535. } else {
  536. /*
  537. * This makes winadmin Idle time happy.
  538. */
  539. (VOID) NtQuerySystemTime( &(pInfo->LastInputTime) );
  540. }
  541. (VOID) NtQuerySystemTime( &pInfo->CurrentTime );
  542. CopyInWireBuf(WinStationInformationClass,
  543. (PVOID)pInfo,
  544. pWinStationInformation);
  545. *pReturnLength = WinStationInformationLength;
  546. MemFree( pIca_Stack_Query_Status );
  547. }
  548. else
  549. {
  550. Status = STATUS_NO_MEMORY;
  551. }
  552. MemFree(pInfo);
  553. }
  554. else
  555. {
  556. Status = STATUS_NO_MEMORY;
  557. }
  558. }
  559. }
  560. break;
  561. case WinStationConfiguration:
  562. if (!ValidWireBuffer(WinStationInformationClass,
  563. pWinStationInformation,
  564. WinStationInformationLength)) {
  565. Status = STATUS_INVALID_USER_BUFFER;
  566. break;
  567. }
  568. CopyInWireBuf(WinStationInformationClass,
  569. (PVOID)&pWinStation->Config.Config,
  570. pWinStationInformation);
  571. if (RpcCheckSystemClientEx( pWinStation ) != STATUS_SUCCESS) {
  572. PWINSTACONFIGWIREW p = pWinStationInformation;
  573. PUSERCONFIGW u = (PUSERCONFIGW)((PCHAR)p + p->UserConfig.Offset);
  574. RtlZeroMemory( &u->Password, sizeof(u->Password) );
  575. }
  576. *pReturnLength = WinStationInformationLength;
  577. break;
  578. case WinStationWd:
  579. if (!ValidWireBuffer(WinStationInformationClass,
  580. pWinStationInformation,
  581. WinStationInformationLength)){
  582. Status = STATUS_INVALID_USER_BUFFER;
  583. break;
  584. }
  585. CopyInWireBuf(WinStationInformationClass,
  586. (PVOID)&pWinStation->Config.Wd,
  587. pWinStationInformation);
  588. *pReturnLength = WinStationInformationLength;
  589. break;
  590. case WinStationPd:
  591. if (!ValidWireBuffer(WinStationInformationClass,
  592. pWinStationInformation,
  593. WinStationInformationLength)){
  594. Status = STATUS_INVALID_USER_BUFFER;
  595. break;
  596. }
  597. CopyInWireBuf(WinStationInformationClass,
  598. (PVOID)&pWinStation->Config.Pd[0],
  599. pWinStationInformation);
  600. *pReturnLength = WinStationInformationLength;
  601. break;
  602. case WinStationCd:
  603. if ( WinStationInformationLength > sizeof(CDCONFIG) )
  604. WinStationInformationLength = sizeof(CDCONFIG);
  605. memcpy( pWinStationInformation,
  606. &pWinStation->Config.Cd,
  607. WinStationInformationLength );
  608. *pReturnLength = WinStationInformationLength;
  609. break;
  610. case WinStationPdParams:
  611. {
  612. if (!ValidWireBuffer(WinStationInformationClass,
  613. pWinStationInformation,
  614. WinStationInformationLength)){
  615. Status = STATUS_INVALID_USER_BUFFER;
  616. break;
  617. }
  618. else
  619. {
  620. PDPARAMS *pPdParams;
  621. pPdParams = MemAlloc( sizeof( PDPARAMS ) );
  622. if (pPdParams)
  623. {
  624. CopyOutWireBuf(WinStationInformationClass,
  625. (PVOID) pPdParams,
  626. pWinStationInformation);
  627. /*
  628. * Based on PDClass, this can query any PD
  629. */
  630. if ( pWinStation->hStack &&
  631. pWinStation->pWsx &&
  632. pWinStation->pWsx->pWsxIcaStackIoControl ) {
  633. Status = pWinStation->pWsx->pWsxIcaStackIoControl(
  634. pWinStation->pWsxContext,
  635. pWinStation->hIca,
  636. pWinStation->hStack,
  637. IOCTL_ICA_STACK_QUERY_PARAMS,
  638. pPdParams,
  639. sizeof(PDPARAMS ),
  640. pPdParams,
  641. sizeof( PDPARAMS ),
  642. pReturnLength );
  643. /*
  644. * If we get an error in the idle/disconnected state,
  645. * or if this is a session on the local console.
  646. * then just clear the return buffer and return success.
  647. */
  648. if ( !NT_SUCCESS( Status ) ) {
  649. if ((pWinStation->fOwnsConsoleTerminal) ||
  650. (pWinStation->State != State_Active &&
  651. pWinStation->State != State_Connected )) {
  652. memset(pPdParams, 0, sizeof(PDPARAMS));
  653. *pReturnLength = WinStationInformationLength;
  654. Status = STATUS_SUCCESS;
  655. }
  656. }
  657. } else {
  658. memset( (PVOID)pPdParams, 0, sizeof(PDPARAMS) );
  659. *pReturnLength = WinStationInformationLength;
  660. Status = STATUS_SUCCESS;
  661. }
  662. if (NT_SUCCESS(Status)) {
  663. CopyInWireBuf(WinStationInformationClass,
  664. (PVOID)pPdParams,
  665. pWinStationInformation);
  666. }
  667. *pReturnLength = WinStationInformationLength;
  668. MemFree( pPdParams );
  669. }
  670. else
  671. {
  672. Status = STATUS_NO_MEMORY;
  673. }
  674. }
  675. }
  676. break;
  677. case WinStationClient:
  678. if (!ValidWireBuffer(WinStationInformationClass,
  679. pWinStationInformation,
  680. WinStationInformationLength)){
  681. Status = STATUS_INVALID_USER_BUFFER;
  682. break;
  683. }
  684. CopyInWireBuf(WinStationInformationClass,
  685. (PVOID)&pWinStation->Client,
  686. pWinStationInformation);
  687. // if caller is not allow to see it, then scrub the password
  688. if ( !IsCallerAllowedPasswordAccess() ) {
  689. PWINSTATIONCLIENT pWSClient = (PWINSTATIONCLIENT)pWinStationInformation;
  690. PBYTE pStart;
  691. PBYTE pEnd;
  692. ULONG ulMaxToScrub;
  693. pEnd = (PBYTE) ( pWinStationInformation ) + WinStationInformationLength;
  694. if ((ULONG_PTR) pEnd > (ULONG_PTR)pWSClient->Password) {
  695. ulMaxToScrub = (ULONG)((ULONG_PTR) pEnd - (ULONG_PTR)pWSClient->Password);
  696. if (ulMaxToScrub > sizeof(pWSClient->Password))
  697. ulMaxToScrub = sizeof(pWSClient->Password);
  698. memset(pWSClient->Password, 0,ulMaxToScrub);
  699. }
  700. }
  701. *pReturnLength = WinStationInformationLength;
  702. break;
  703. case WinStationModules:
  704. // Check for availability
  705. if (pWinStation->hStack &&
  706. pWinStation->pWsx &&
  707. pWinStation->pWsx->pWsxIcaStackIoControl) {
  708. ULONG b = (ULONG) IsCallerAllowedPasswordAccess();
  709. Status = pWinStation->pWsx->pWsxIcaStackIoControl(
  710. pWinStation->pWsxContext,
  711. pWinStation->hIca,
  712. pWinStation->hStack,
  713. IOCTL_ICA_STACK_QUERY_MODULE_DATA,
  714. (PVOID) &b,
  715. sizeof(b),
  716. pWinStationInformation,
  717. WinStationInformationLength,
  718. pReturnLength );
  719. } else {
  720. memset( pWinStationInformation, 0, WinStationInformationLength );
  721. Status = STATUS_SUCCESS;
  722. }
  723. break;
  724. case WinStationCreateData:
  725. if ( WinStationInformationLength > sizeof(WINSTATIONCREATE) )
  726. WinStationInformationLength = sizeof(WINSTATIONCREATE);
  727. memcpy( pWinStationInformation,
  728. &pWinStation->Config.Create,
  729. WinStationInformationLength );
  730. *pReturnLength = WinStationInformationLength;
  731. break;
  732. case WinStationPrinter:
  733. Status = STATUS_INVALID_DEVICE_REQUEST;
  734. break;
  735. case WinStationUserToken:
  736. if ( WinStationInformationLength < sizeof(WINSTATIONUSERTOKEN) ) {
  737. Status = STATUS_BUFFER_TOO_SMALL;
  738. break;
  739. }
  740. /*
  741. * Check it for WINSTATION_ALL_ACCESS. This will generate an
  742. * access audit if on.
  743. */
  744. Status = RpcCheckClientAccess( pWinStation, WINSTATION_ALL_ACCESS, FALSE );
  745. if ( !NT_SUCCESS( Status ) ) {
  746. break;
  747. }
  748. //
  749. // Make sure only system mode callers can get this token.
  750. //
  751. // A Token is a very dangerous thing to allow someone to
  752. // get a hold of, since they can create processes that
  753. // have the tokens subject context.
  754. //
  755. Status = RpcCheckSystemClientNoLogonId( pWinStation );
  756. if (!NT_SUCCESS(Status)) {
  757. break;
  758. }
  759. Status = xxxGetUserToken(
  760. pWinStation,
  761. (WINSTATIONUSERTOKEN UNALIGNED *)pWinStationInformation,
  762. WinStationInformationLength
  763. );
  764. *pReturnLength = sizeof(WINSTATIONUSERTOKEN);
  765. break;
  766. case WinStationVideoData:
  767. if ( !pWinStation->LogonId || !pWinStation->hStack ) {
  768. Status = STATUS_PROCEDURE_NOT_FOUND;
  769. break;
  770. }
  771. if ( WinStationInformationLength < sizeof(WINSTATIONVIDEODATA) ) {
  772. Status = STATUS_BUFFER_TOO_SMALL;
  773. break;
  774. }
  775. pVideoData = (PWINSTATIONVIDEODATA) pWinStationInformation;
  776. pVideoData->HResolution = pWinStation->Client.HRes;
  777. pVideoData->VResolution = pWinStation->Client.VRes;
  778. pVideoData->fColorDepth = pWinStation->Client.ColorDepth;
  779. *pReturnLength = sizeof(WINSTATIONVIDEODATA);
  780. break;
  781. case WinStationVirtualData:
  782. if ( !pWinStation->hStack ) {
  783. Status = STATUS_INVALID_DEVICE_REQUEST;
  784. break;
  785. }
  786. if ( WinStationInformationLength < sizeof(VIRTUALCHANNELNAME) ) {
  787. Status = STATUS_BUFFER_TOO_SMALL;
  788. break;
  789. }
  790. /*
  791. * Open virtual channel handle
  792. */
  793. Status = IcaChannelOpen( pWinStation->hIca,
  794. Channel_Virtual,
  795. pWinStationInformation,
  796. &hVirtual );
  797. if ( !NT_SUCCESS( Status ) )
  798. break;
  799. /*
  800. * Query client virtual channel data
  801. */
  802. Status = IcaChannelIoControl( hVirtual,
  803. IOCTL_ICA_VIRTUAL_QUERY_MODULE_DATA,
  804. NULL,
  805. 0,
  806. pWinStationInformation,
  807. WinStationInformationLength,
  808. pReturnLength );
  809. /*
  810. * Close virtual channel
  811. */
  812. IcaChannelClose(hVirtual);
  813. break;
  814. case WinStationLoadBalanceSessionTarget:
  815. // This query requests the target session ID for a
  816. // client redirected from another server in a load balancing
  817. // cluster. Returns -1 for no redirection. This call is
  818. // normally made only by WinLogon.
  819. if (WinStationInformationLength > sizeof(ULONG))
  820. WinStationInformationLength = sizeof(ULONG);
  821. if (!pWinStation->bRequestedSessionIDFieldValid)
  822. *((ULONG *)pWinStationInformation) = (ULONG)-1;
  823. else
  824. *((ULONG *)pWinStationInformation) =
  825. pWinStation->RequestedSessionID;
  826. *pReturnLength = WinStationInformationLength;
  827. break;
  828. case WinStationShadowInfo:
  829. {
  830. PWINSTATIONSHADOW pWinstationShadow;
  831. if (WinStationInformationLength >= sizeof(WINSTATIONSHADOW)) {
  832. pWinstationShadow = (PWINSTATIONSHADOW) pWinStationInformation;
  833. if ( pWinStation->State == State_Shadow ) {
  834. // The current state is Shadow so it's a viewer
  835. pWinstationShadow->ShadowState = State_Shadowing;
  836. } else if ( pWinStation->State == State_Active &&
  837. !IsListEmpty(&pWinStation->ShadowHead) ) {
  838. // Active and being shadowed
  839. pWinstationShadow->ShadowState = State_Shadowed;
  840. } else {
  841. pWinstationShadow->ShadowState = State_NoShadow;
  842. }
  843. pWinstationShadow->ShadowClass = pWinStation->Config.Config.User.Shadow;
  844. pWinstationShadow->SessionId = LogonId;
  845. pWinstationShadow->ProtocolType = pWinStation->Client.ProtocolType;
  846. *pReturnLength = sizeof(WINSTATIONSHADOW);
  847. }
  848. else {
  849. Status = STATUS_BUFFER_TOO_SMALL;
  850. }
  851. }
  852. break;
  853. case WinStationDigProductId:
  854. {
  855. PWINSTATIONPRODID pWinStationProdId;
  856. if ( WinStationInformationLength >= sizeof(WINSTATIONPRODID) )
  857. {
  858. pWinStationProdId = (PWINSTATIONPRODID)pWinStationInformation;
  859. memcpy( pWinStationProdId->DigProductId, g_DigProductId, sizeof( g_DigProductId ));
  860. memcpy( pWinStationProdId->ClientDigProductId, pWinStation->Client.clientDigProductId, sizeof( pWinStation->Client.clientDigProductId ));
  861. pWinStationProdId->curentSessionId = pWinStation->LogonId;
  862. pWinStationProdId->ClientSessionId = pWinStation->Client.ClientSessionId;
  863. *pReturnLength = WinStationInformationLength;
  864. }
  865. else
  866. {
  867. Status = STATUS_BUFFER_TOO_SMALL;
  868. }
  869. break;
  870. }
  871. case WinStationLockedState:
  872. {
  873. BOOL bLockedState;
  874. if ( pWinStationInformation && (WinStationInformationLength >= sizeof(bLockedState)))
  875. {
  876. Status = GetLockedState(pWinStation, &bLockedState);
  877. *(LPBOOL)pWinStationInformation = bLockedState;
  878. *pReturnLength = sizeof(bLockedState);
  879. }
  880. else
  881. {
  882. Status = STATUS_BUFFER_TOO_SMALL;
  883. }
  884. break;
  885. }
  886. case WinStationRemoteAddress:
  887. {
  888. PWINSTATIONREMOTEADDRESS pRemoteAddress = (PWINSTATIONREMOTEADDRESS) pWinStationInformation;
  889. if( WinStationInformationLength >= sizeof(WINSTATIONREMOTEADDRESS) )
  890. {
  891. Status = xxxQueryRemoteAddress( pWinStation, pRemoteAddress );
  892. }
  893. else
  894. {
  895. *pReturnLength = sizeof(WINSTATIONREMOTEADDRESS);
  896. Status = STATUS_BUFFER_TOO_SMALL;
  897. }
  898. break;
  899. }
  900. case WinStationLastReconnectType:
  901. {
  902. if ( pWinStationInformation && (WinStationInformationLength >= sizeof(ULONG)))
  903. {
  904. *((ULONG *)pWinStationInformation) = pWinStation->LastReconnectType;
  905. *pReturnLength = sizeof(ULONG);
  906. }
  907. else
  908. {
  909. Status = STATUS_BUFFER_TOO_SMALL;
  910. }
  911. break;
  912. }
  913. case WinStationMprNotifyInfo:
  914. {
  915. pExtendedClientCredentials pMprNotifyInfo;
  916. // Only System can query this information
  917. Status = _CheckCallerLocalAndSystem();
  918. if (Status != STATUS_SUCCESS) {
  919. break;
  920. }
  921. if (WinStationInformationLength >= sizeof(ExtendedClientCredentials)) {
  922. pMprNotifyInfo = (pExtendedClientCredentials) pWinStationInformation;
  923. *pMprNotifyInfo = g_MprNotifyInfo;
  924. *pReturnLength = sizeof(ExtendedClientCredentials);
  925. // Erase the sensitive information now since its no longer needed in TermSrv
  926. RtlSecureZeroMemory( g_MprNotifyInfo.Domain, wcslen(g_MprNotifyInfo.Domain) * sizeof(WCHAR) );
  927. RtlSecureZeroMemory( g_MprNotifyInfo.UserName, wcslen(g_MprNotifyInfo.UserName) * sizeof(WCHAR) );
  928. RtlSecureZeroMemory( g_MprNotifyInfo.Password, wcslen(g_MprNotifyInfo.Password) * sizeof(WCHAR) );
  929. } else {
  930. Status = STATUS_BUFFER_TOO_SMALL;
  931. }
  932. }
  933. break;
  934. default:
  935. /*
  936. * Fail the call
  937. */
  938. Status = STATUS_INVALID_INFO_CLASS;
  939. break;
  940. }
  941. ReleaseWinStation(pWinStation);
  942. TRACE((hTrace,TC_ICASRV,TT_API2,"TERMSRV: WinStationQueryInformation "
  943. "LogonId=%d, Class=%d, Status=0x%x\n",
  944. LogonId, (ULONG)WinStationInformationClass, Status));
  945. return Status;
  946. }
  947. /*****************************************************************************
  948. * xxxGetUserToken
  949. *
  950. * Duplicate the users token into the process space of the caller
  951. * if they are an admin.
  952. *
  953. * ENTRY:
  954. * p (input/output)
  955. * Argument buffer
  956. *
  957. * Length (input)
  958. * Size of argument buffer
  959. ****************************************************************************/
  960. NTSTATUS xxxGetUserToken(
  961. PWINSTATION pWinStation,
  962. WINSTATIONUSERTOKEN UNALIGNED *p,
  963. ULONG Size)
  964. {
  965. NTSTATUS Status;
  966. HANDLE RemoteToken;
  967. HANDLE RemoteProcess;
  968. CLIENT_ID ClientId;
  969. OBJECT_ATTRIBUTES ObjA;
  970. // Determine if the caller is an admin
  971. //
  972. // If the token is not NULL, duplicate it into the callers
  973. // process space.
  974. //
  975. if (pWinStation->UserToken == NULL) {
  976. return STATUS_NO_TOKEN;
  977. }
  978. InitializeObjectAttributes(&ObjA, NULL, 0, NULL, NULL);
  979. ClientId.UniqueProcess = p->ProcessId;
  980. ClientId.UniqueThread = p->ThreadId;
  981. Status = NtOpenProcess(
  982. &RemoteProcess,
  983. PROCESS_ALL_ACCESS,
  984. &ObjA,
  985. &ClientId);
  986. if (!NT_SUCCESS(Status)) {
  987. TRACE((hTrace,TC_ICASRV,TT_ERROR,"TermSrv GETTOKEN: Error 0x%x "
  988. "opening remote process %d\n", Status,p->ProcessId));
  989. return Status;
  990. }
  991. Status = NtDuplicateObject(
  992. NtCurrentProcess(),
  993. pWinStation->UserToken,
  994. RemoteProcess,
  995. &RemoteToken,
  996. 0,
  997. 0,
  998. DUPLICATE_SAME_ACCESS);
  999. if (!NT_SUCCESS(Status)) {
  1000. TRACE((hTrace,TC_ICASRV,TT_ERROR, "TermSrv GETTOKEN: Error 0x%x "
  1001. "duplicating UserToken\n", Status));
  1002. NtClose( RemoteProcess );
  1003. return Status;
  1004. }
  1005. p->UserToken = RemoteToken;
  1006. NtClose(RemoteProcess);
  1007. return STATUS_SUCCESS;
  1008. }