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.

550 lines
14 KiB

  1. /*******************************************************************************
  2. * wtsapi32.c
  3. *
  4. * Published Terminal Server APIs
  5. *
  6. * Copyright 1998, Citrix Systems Inc.
  7. * Copyright (C) 1997-1999 Microsoft Corp.
  8. /******************************************************************************/
  9. #include <nt.h>
  10. #include <ntrtl.h>
  11. #include <nturtl.h>
  12. #include <ntddkbd.h>
  13. #include <ntddmou.h>
  14. #include <windows.h>
  15. #include <winbase.h>
  16. #include <winerror.h>
  17. #if(WINVER >= 0x0500)
  18. #include <ntstatus.h>
  19. #include <winsta.h>
  20. #else
  21. #include <citrix\cxstatus.h>
  22. #include <citrix\winsta.h>
  23. #endif
  24. #include <utildll.h>
  25. #include <stdio.h>
  26. #include <stdarg.h>
  27. #include <wtsapi32.h>
  28. // Private User function that returns user token for session 0 only
  29. // Used in the case when TS is not running
  30. extern
  31. HANDLE
  32. GetCurrentUserTokenW (
  33. WCHAR Winsta[],
  34. DWORD DesiredAccess
  35. );
  36. /*=============================================================================
  37. == External procedures defined
  38. =============================================================================*/
  39. BOOL WINAPI WTSShutdownSystem( HANDLE, DWORD );
  40. BOOL WINAPI WTSWaitSystemEvent( HANDLE, DWORD, DWORD * );
  41. VOID WINAPI WTSFreeMemory( PVOID pMemory );
  42. BOOL WINAPI WTSQueryUserToken(ULONG SessionId, PHANDLE phToken);
  43. /*=============================================================================
  44. == Internal procedures defined
  45. =============================================================================*/
  46. BOOL WINAPI DllEntryPoint( HINSTANCE, DWORD, LPVOID );
  47. BOOL IsTerminalServiceRunning(VOID);
  48. BOOL IsProcessPrivileged(CONST PCWSTR szPrivilege);
  49. /*=============================================================================
  50. == Local function prototypes
  51. =============================================================================*/
  52. BOOLEAN CheckShutdownPrivilege();
  53. /****************************************************************************
  54. *
  55. * WTSShutdowSystem
  56. *
  57. * Shutdown and/or reboot system
  58. *
  59. * ENTRY:
  60. * hServer (input)
  61. * Terminal Server handle (or WTS_CURRENT_SERVER)
  62. * ShutdownFlags (input)
  63. * Flags which specify shutdown options.
  64. *
  65. * EXIT:
  66. *
  67. * TRUE -- The operation succeeded.
  68. *
  69. * FALSE -- The operation failed. Extended error status is available
  70. * using GetLastError.
  71. *
  72. ****************************************************************************/
  73. BOOL
  74. WINAPI
  75. WTSShutdownSystem(
  76. IN HANDLE hServer,
  77. IN DWORD ShutdownFlags
  78. )
  79. {
  80. ULONG uiOptions = 0;
  81. // Make sure the user has the proper privilege to shutdown the system when
  82. // hServer is a local server handle. For remote server, the user privilege
  83. // is checked when WTSOpenServer is called.
  84. if (hServer == SERVERNAME_CURRENT && !CheckShutdownPrivilege()) {
  85. SetLastError(ERROR_PRIVILEGE_NOT_HELD);
  86. return(FALSE);
  87. }
  88. // Construct the shutdown flag
  89. if (ShutdownFlags == WTS_WSD_LOGOFF) {
  90. // log off all users and deletes sessions
  91. uiOptions = WSD_LOGOFF;
  92. } else if (ShutdownFlags == WTS_WSD_SHUTDOWN) {
  93. uiOptions = WSD_LOGOFF | WSD_SHUTDOWN;
  94. } else if (ShutdownFlags == WTS_WSD_REBOOT) {
  95. uiOptions = WSD_LOGOFF | WSD_SHUTDOWN | WSD_REBOOT;
  96. } else if (ShutdownFlags == WTS_WSD_POWEROFF) {
  97. uiOptions = WSD_LOGOFF | WSD_SHUTDOWN | WSD_POWEROFF;
  98. } else if (ShutdownFlags == WTS_WSD_FASTREBOOT) {
  99. uiOptions = WSD_FASTREBOOT | WSD_REBOOT;
  100. } else {
  101. SetLastError(ERROR_INVALID_PARAMETER);
  102. return FALSE;
  103. }
  104. return( WinStationShutdownSystem( hServer, uiOptions ));
  105. }
  106. /****************************************************************************
  107. *
  108. * WTSWaitSystemEvent
  109. *
  110. * Waits for an event (WinStation create, delete, connect, etc) before
  111. * returning to the caller.
  112. *
  113. * ENTRY:
  114. * hServer (input)
  115. * Terminal Server handle (or WTS_CURRENT_SERVER)
  116. * EventFlags (input)
  117. * Bit mask that specifies which event(s) to wait for (WTS_EVENT_?)
  118. * pEventFlags (output)
  119. * Bit mask of event(s) that occurred.
  120. *
  121. * EXIT:
  122. *
  123. * TRUE -- The operation succeeded.
  124. *
  125. * FALSE -- The operation failed. Extended error status is available
  126. * using GetLastError.
  127. *
  128. ****************************************************************************/
  129. BOOL
  130. WINAPI
  131. WTSWaitSystemEvent(
  132. IN HANDLE hServer,
  133. IN DWORD EventMask,
  134. OUT DWORD * pEventFlags
  135. )
  136. {
  137. BOOL fSuccess;
  138. ULONG WSEventMask;
  139. ULONG WSEventFlags = 0;
  140. if (IsBadWritePtr(pEventFlags, sizeof(DWORD))) {
  141. SetLastError(ERROR_INVALID_PARAMETER);
  142. return FALSE;
  143. }
  144. /*
  145. * Map event mask
  146. */
  147. WSEventMask = 0;
  148. if ( EventMask & WTS_EVENT_CREATE )
  149. WSEventMask |= WEVENT_CREATE;
  150. if ( EventMask & WTS_EVENT_DELETE )
  151. WSEventMask |= WEVENT_DELETE;
  152. if ( EventMask & WTS_EVENT_RENAME )
  153. WSEventMask |= WEVENT_RENAME;
  154. if ( EventMask & WTS_EVENT_CONNECT )
  155. WSEventMask |= WEVENT_CONNECT;
  156. if ( EventMask & WTS_EVENT_DISCONNECT )
  157. WSEventMask |= WEVENT_DISCONNECT;
  158. if ( EventMask & WTS_EVENT_LOGON )
  159. WSEventMask |= WEVENT_LOGON;
  160. if ( EventMask & WTS_EVENT_LOGOFF )
  161. WSEventMask |= WEVENT_LOGOFF;
  162. if ( EventMask & WTS_EVENT_STATECHANGE )
  163. WSEventMask |= WEVENT_STATECHANGE;
  164. if ( EventMask & WTS_EVENT_LICENSE )
  165. WSEventMask |= WEVENT_LICENSE;
  166. if ( EventMask & WTS_EVENT_FLUSH )
  167. WSEventMask |= WEVENT_FLUSH;
  168. /*
  169. * Wait for system event
  170. */
  171. fSuccess = WinStationWaitSystemEvent( hServer, WSEventMask, &WSEventFlags );
  172. /*
  173. * Map event mask
  174. */
  175. *pEventFlags = 0;
  176. if ( WSEventFlags & WEVENT_CREATE )
  177. *pEventFlags |= WTS_EVENT_CREATE;
  178. if ( WSEventFlags & WEVENT_DELETE )
  179. *pEventFlags |= WTS_EVENT_DELETE;
  180. if ( WSEventFlags & WEVENT_RENAME )
  181. *pEventFlags |= WTS_EVENT_RENAME;
  182. if ( WSEventFlags & WEVENT_CONNECT )
  183. *pEventFlags |= WTS_EVENT_CONNECT;
  184. if ( WSEventFlags & WEVENT_DISCONNECT )
  185. *pEventFlags |= WTS_EVENT_DISCONNECT;
  186. if ( WSEventFlags & WEVENT_LOGON )
  187. *pEventFlags |= WTS_EVENT_LOGON;
  188. if ( WSEventFlags & WEVENT_LOGOFF )
  189. *pEventFlags |= WTS_EVENT_LOGOFF;
  190. if ( WSEventFlags & WEVENT_STATECHANGE )
  191. *pEventFlags |= WTS_EVENT_STATECHANGE;
  192. if ( WSEventFlags & WEVENT_LICENSE )
  193. *pEventFlags |= WTS_EVENT_LICENSE;
  194. return( fSuccess );
  195. }
  196. /****************************************************************************
  197. *
  198. * WTSFreeMemory
  199. *
  200. * Free memory allocated by Terminal Server APIs
  201. *
  202. * ENTRY:
  203. * pMemory (input)
  204. * Pointer to memory to free
  205. *
  206. * EXIT:
  207. * nothing
  208. *
  209. ****************************************************************************/
  210. VOID
  211. WINAPI
  212. WTSFreeMemory( PVOID pMemory )
  213. {
  214. LocalFree( pMemory );
  215. }
  216. /****************************************************************************
  217. *
  218. * DllEntryPoint
  219. *
  220. * Function is called when the DLL is loaded and unloaded.
  221. *
  222. * ENTRY:
  223. * hinstDLL (input)
  224. * Handle of DLL module
  225. * fdwReason (input)
  226. * Why function was called
  227. * lpvReserved (input)
  228. * Reserved; must be NULL
  229. *
  230. * EXIT:
  231. * TRUE - Success
  232. * FALSE - Error occurred
  233. *
  234. ****************************************************************************/
  235. BOOL WINAPI
  236. DllEntryPoint( HINSTANCE hinstDLL,
  237. DWORD fdwReason,
  238. LPVOID lpvReserved )
  239. {
  240. switch ( fdwReason ) {
  241. case DLL_PROCESS_ATTACH:
  242. break;
  243. case DLL_PROCESS_DETACH:
  244. break;
  245. default:
  246. break;
  247. }
  248. return( TRUE );
  249. }
  250. /*****************************************************************************
  251. *
  252. * CheckShutdownPrivilege
  253. *
  254. * Check whether the current process has shutdown permission.
  255. *
  256. * ENTRY:
  257. *
  258. * EXIT:
  259. *
  260. *
  261. ****************************************************************************/
  262. BOOLEAN
  263. CheckShutdownPrivilege()
  264. {
  265. NTSTATUS Status;
  266. BOOLEAN WasEnabled;
  267. //
  268. // Try the thread token first
  269. //
  270. Status = RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE,
  271. TRUE,
  272. TRUE,
  273. &WasEnabled);
  274. if (Status == STATUS_NO_TOKEN) {
  275. //
  276. // No thread token, use the process token
  277. //
  278. Status = RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE,
  279. TRUE,
  280. FALSE,
  281. &WasEnabled);
  282. }
  283. if (!NT_SUCCESS(Status)) {
  284. return(FALSE);
  285. }
  286. return(TRUE);
  287. }
  288. /*++
  289. Routine Description:
  290. Allows to read the token of the user interactively logged in the session identified by SessionId.
  291. The caller must be running under local system account and hold SE_TCB_NAME privilege. This API
  292. is intended for highly trusted services. Service Providers using it must be very cautious not to
  293. leak user tokens.
  294. NOTE : The API is RPC based and hence cannot be called with the loader lock held (specifically
  295. from DLL attach/detach code)
  296. Arguments:
  297. SessionId: IN. Identifies the session the user is logged in.
  298. phToken: OUT. Points to the user token handle, if the function succeeded.
  299. Return Values:
  300. TRUE in case of success. phToken points to the user token.
  301. FALSE in case of failure. Use GetLastError() to get extended error code.
  302. The token returned is a duplicate of a primary token.
  303. --*/
  304. BOOL
  305. WINAPI
  306. WTSQueryUserToken(ULONG SessionId, PHANDLE phToken)
  307. {
  308. BOOL IsTsUp = FALSE;
  309. BOOL Result, bHasPrivilege;
  310. ULONG ReturnLength;
  311. WINSTATIONUSERTOKEN Info;
  312. NTSTATUS Status;
  313. HANDLE hUserToken = NULL;
  314. SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
  315. // Do parameter Validation
  316. if (NULL == phToken) {
  317. SetLastError(ERROR_INVALID_PARAMETER);
  318. return(FALSE);
  319. }
  320. // We will first check if the process which is calling us, has SE_TCB_NAME privilege
  321. bHasPrivilege = IsProcessPrivileged(SE_TCB_NAME);
  322. if (!bHasPrivilege) {
  323. SetLastError(ERROR_PRIVILEGE_NOT_HELD);
  324. return(FALSE);
  325. }
  326. // If it is session 0, don't call winsta. Use GetCurrentUserToken instead.
  327. if (SessionId == 0)
  328. {
  329. hUserToken = GetCurrentUserTokenW(L"WinSta0",
  330. TOKEN_QUERY |
  331. TOKEN_DUPLICATE |
  332. TOKEN_ASSIGN_PRIMARY
  333. );
  334. if (hUserToken == NULL)
  335. return FALSE;
  336. else
  337. *phToken = hUserToken;
  338. }
  339. else // Non-zero sessions
  340. {
  341. // No one except TS has any idea about non-zero sessions. So, check if the TS is running.
  342. IsTsUp = IsTerminalServiceRunning();
  343. if (IsTsUp)
  344. { // This is so that CSRSS can dup the handle to our process
  345. Info.ProcessId = LongToHandle(GetCurrentProcessId());
  346. Info.ThreadId = LongToHandle(GetCurrentThreadId());
  347. Result = WinStationQueryInformation(
  348. SERVERNAME_CURRENT,
  349. SessionId,
  350. WinStationUserToken,
  351. &Info,
  352. sizeof(Info),
  353. &ReturnLength
  354. );
  355. if( !Result )
  356. return FALSE;
  357. else
  358. *phToken = Info.UserToken ;
  359. }
  360. else
  361. { // TS is not running. So, set error for non-zero sessions: WINSTATION_NOT_FOUND.
  362. SetLastError(ERROR_CTX_WINSTATION_NOT_FOUND);
  363. return FALSE;
  364. }
  365. }
  366. return TRUE;
  367. }
  368. // This function determines if the Terminal Service is currently Running
  369. BOOL IsTerminalServiceRunning (VOID)
  370. {
  371. BOOL bReturn = FALSE;
  372. SC_HANDLE hServiceController;
  373. hServiceController = OpenSCManager(NULL, NULL, GENERIC_READ);
  374. if (hServiceController) {
  375. SC_HANDLE hTermServ ;
  376. hTermServ = OpenService(hServiceController, L"TermService", SERVICE_QUERY_STATUS);
  377. if (hTermServ) {
  378. SERVICE_STATUS tTermServStatus;
  379. if (QueryServiceStatus(hTermServ, &tTermServStatus)) {
  380. bReturn = (tTermServStatus.dwCurrentState == SERVICE_RUNNING);
  381. } else {
  382. CloseServiceHandle(hTermServ);
  383. CloseServiceHandle(hServiceController);
  384. return FALSE;
  385. }
  386. CloseServiceHandle(hTermServ);
  387. } else {
  388. CloseServiceHandle(hServiceController);
  389. return FALSE;
  390. }
  391. CloseServiceHandle(hServiceController);
  392. } else {
  393. return FALSE;
  394. }
  395. return bReturn;
  396. }
  397. /*++
  398. Routine Description:
  399. This function checks to see if the specified privilege is enabled
  400. in the primary access token for the current thread.
  401. Arguments:
  402. szPrivilege - The privilege to be checked for
  403. Return Value:
  404. TRUE if the specified privilege is enabled, FALSE otherwise.
  405. --*/
  406. BOOL
  407. IsProcessPrivileged(
  408. CONST PCWSTR szPrivilege
  409. )
  410. {
  411. LUID luidValue; // LUID (locally unique ID) for the privilege
  412. BOOL bResult = FALSE, bHasPrivilege = FALSE;
  413. HANDLE hToken = NULL;
  414. PRIVILEGE_SET privilegeSet;
  415. // Get the LUID for the privilege from the privilege name
  416. bResult = LookupPrivilegeValue(
  417. NULL,
  418. szPrivilege,
  419. &luidValue
  420. );
  421. if (!bResult) {
  422. return FALSE;
  423. }
  424. // Get the token of the present thread
  425. bResult = OpenThreadToken(
  426. GetCurrentThread(),
  427. MAXIMUM_ALLOWED,
  428. FALSE,
  429. &hToken
  430. );
  431. if (!bResult) {
  432. // We want to use the token for the current process
  433. bResult = OpenProcessToken(
  434. GetCurrentProcess(),
  435. MAXIMUM_ALLOWED,
  436. &hToken
  437. );
  438. if (!bResult) {
  439. return FALSE;
  440. }
  441. }
  442. // And check for the privilege
  443. privilegeSet.PrivilegeCount = 1;
  444. privilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY;
  445. privilegeSet.Privilege[0].Luid = luidValue;
  446. privilegeSet.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
  447. bResult = PrivilegeCheck(hToken, &privilegeSet, &bHasPrivilege);
  448. CloseHandle(hToken);
  449. return (bResult && bHasPrivilege);
  450. }