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.

629 lines
17 KiB

  1. /*******************************************************************************
  2. *
  3. * CTXMON.C
  4. *
  5. * This module contains code to monitor user processes
  6. *
  7. * Copyright Microsoft, 1997
  8. *
  9. * Author:
  10. *
  11. *
  12. *******************************************************************************/
  13. #include "precomp.h"
  14. #pragma hdrstop
  15. #include <ntexapi.h>
  16. #include "winreg.h"
  17. /*=============================================================================
  18. == Local Defines
  19. =============================================================================*/
  20. #define BUFFER_SIZE 32*1024
  21. #define MAXNAME 18
  22. BOOL IsSystemLUID(HANDLE ProcessId);
  23. //
  24. // This table contains common NT system programs
  25. //
  26. DWORD dwNumberofSysProcs = 0;
  27. LPTSTR *SysProcTable;
  28. typedef struct {
  29. HANDLE hProcess;
  30. HANDLE TerminateEvent;
  31. //HWND hwndNotify;
  32. HANDLE Thread;
  33. } USER_PROCESS_MONITOR, * PUSER_PROCESS_MONITOR;
  34. /*=============================================================================
  35. == Internal Procedures
  36. =============================================================================*/
  37. HANDLE OpenUserProcessHandle();
  38. BOOLEAN IsSystemProcess( PSYSTEM_PROCESS_INFORMATION);
  39. /*=============================================================================
  40. == External Procedures
  41. =============================================================================*/
  42. VOID LookupSidUser( PSID pSid, PWCHAR pUserName, PULONG pcbUserName );
  43. HANDLE
  44. ImpersonateUser(
  45. HANDLE UserToken,
  46. HANDLE ThreadHandle
  47. );
  48. BOOL
  49. StopImpersonating(
  50. HANDLE ThreadHandle
  51. );
  52. BOOL CreateSystemProcList ()
  53. {
  54. DWORD dwIndex;
  55. DWORD dwLongestProcName = 0;
  56. DWORD dwSize = 0;
  57. HKEY hKeySysProcs = NULL;
  58. DWORD iValueIndex = 0;
  59. LONG lResult;
  60. const LPCTSTR SYS_PROC_KEY = TEXT("SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\SysProcs");
  61. // to start with.
  62. SysProcTable = NULL;
  63. dwNumberofSysProcs = 0;
  64. lResult = RegOpenKeyEx(
  65. HKEY_LOCAL_MACHINE, // handle of open key
  66. SYS_PROC_KEY, // address of name of subkey to open
  67. 0 , // reserved
  68. KEY_READ, // security access mask
  69. &hKeySysProcs // address of handle of open key
  70. );
  71. if (lResult != ERROR_SUCCESS)
  72. {
  73. return FALSE;
  74. }
  75. lResult = RegQueryInfoKey(
  76. hKeySysProcs, // handle to key
  77. NULL, // class buffer
  78. NULL, // size of class buffer
  79. NULL, // reserved
  80. NULL, // number of subkeys
  81. NULL, // longest subkey name
  82. NULL, // longest class string
  83. &dwNumberofSysProcs, // number of value entries
  84. &dwLongestProcName, // longest value name
  85. NULL, // longest value data
  86. NULL, // descriptor length
  87. NULL // last write time
  88. );
  89. if (lResult != ERROR_SUCCESS || dwNumberofSysProcs == 0)
  90. {
  91. dwNumberofSysProcs = 0;
  92. RegCloseKey(hKeySysProcs);
  93. return FALSE;
  94. }
  95. dwLongestProcName += 1; // provision for the terminating null
  96. SysProcTable = LocalAlloc(LPTR, sizeof(LPTSTR) * dwNumberofSysProcs);
  97. if (!SysProcTable)
  98. {
  99. SysProcTable = NULL;
  100. dwNumberofSysProcs = 0;
  101. RegCloseKey(hKeySysProcs);
  102. return FALSE;
  103. }
  104. for (dwIndex = 0; dwIndex < dwNumberofSysProcs; dwIndex++)
  105. {
  106. SysProcTable[dwIndex] = (LPTSTR) LocalAlloc(LPTR, dwLongestProcName * sizeof(TCHAR));
  107. if (SysProcTable[dwIndex] == NULL)
  108. {
  109. //
  110. // if we failed to alloc bail out.
  111. //
  112. while (dwIndex)
  113. {
  114. LocalFree(SysProcTable[dwIndex-1]);
  115. dwIndex--;
  116. }
  117. LocalFree(SysProcTable);
  118. SysProcTable = NULL;
  119. dwNumberofSysProcs = 0;
  120. RegCloseKey(hKeySysProcs);
  121. return FALSE;
  122. }
  123. }
  124. iValueIndex = 0;
  125. while (iValueIndex < dwNumberofSysProcs)
  126. {
  127. dwSize = dwLongestProcName;
  128. lResult = RegEnumValue(
  129. hKeySysProcs, // handle of key to query
  130. iValueIndex, // index of value to query
  131. SysProcTable[iValueIndex], // address of buffer for value string
  132. &dwSize, // address for size of value buffer
  133. 0, // reserved
  134. NULL, // address of buffer for type code
  135. NULL, // address of buffer for value data
  136. NULL // address for size of data buffer
  137. );
  138. if (lResult != ERROR_SUCCESS)
  139. {
  140. lstrcpy(SysProcTable[iValueIndex], TEXT("")); // this is an invalid entry.
  141. if (lResult == ERROR_NO_MORE_ITEMS)
  142. break;
  143. }
  144. iValueIndex++;
  145. }
  146. return TRUE;
  147. }
  148. void DestroySystemprocList ()
  149. {
  150. DWORD dwIndex;
  151. if (SysProcTable)
  152. {
  153. for (dwIndex = 0; dwIndex < dwNumberofSysProcs; dwIndex++)
  154. {
  155. if (SysProcTable[dwIndex])
  156. {
  157. LocalFree(SysProcTable[dwIndex]);
  158. }
  159. }
  160. LocalFree(SysProcTable);
  161. SysProcTable = NULL;
  162. dwNumberofSysProcs = 0;
  163. }
  164. }
  165. /***************************************************************************\
  166. * FUNCTION: UserProcessMonitorThread
  167. *
  168. * Thread monitoring user processes. It intiates a LOGOFF when there
  169. * are no more user processes.
  170. *
  171. *
  172. \***************************************************************************/
  173. DWORD UserProcessMonitorThread(
  174. LPVOID lpThreadParameter
  175. )
  176. {
  177. PUSER_PROCESS_MONITOR pMonitor = (PUSER_PROCESS_MONITOR)lpThreadParameter;
  178. HANDLE ImpersonationHandle;
  179. DWORD WaitResult;
  180. HANDLE WaitHandle;
  181. HKEY hKey;
  182. DWORD dwVal = 0;
  183. //This value should be per user
  184. if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\WOW", 0,
  185. KEY_WRITE, &hKey) == ERROR_SUCCESS) {
  186. //
  187. // Set the SharedWowTimeout to zero so that WOW exits as soon as all the 16 bit
  188. // processes are gone
  189. //
  190. RegSetValueEx (hKey, L"SharedWowTimeout", 0, REG_DWORD, (LPBYTE)&dwVal,
  191. sizeof(DWORD));
  192. RegCloseKey(hKey);
  193. }
  194. if (!CreateSystemProcList ())
  195. DebugLog((DEB_ERROR, "ERROR, CreateSystemProcList failed.\n", GetLastError()));
  196. for (;;) {
  197. if ( !(pMonitor->hProcess = OpenUserProcessHandle()) ) {
  198. break;
  199. }
  200. // Wait for process to exit or terminate event to be signaled
  201. WaitResult = WaitForMultipleObjects( 2, &pMonitor->hProcess,
  202. FALSE, (DWORD)-1 );
  203. if ( WaitResult == 1 ) { // if terminate event was signaled
  204. CloseHandle( pMonitor->hProcess );
  205. DestroySystemprocList ();
  206. return(0);
  207. }
  208. }
  209. DestroySystemprocList ();
  210. //
  211. // Initiate logoff
  212. //
  213. ImpersonationHandle = ImpersonateUser(g_UserToken , NULL );
  214. if( ImpersonationHandle ) {
  215. ExitWindowsEx(EWX_LOGOFF, 0);
  216. StopImpersonating(ImpersonationHandle);
  217. }
  218. WaitForSingleObject( pMonitor->TerminateEvent, (DWORD)-1 );
  219. return(0);
  220. }
  221. /***************************************************************************\
  222. * FUNCTION: StartUserProcessMonitor
  223. *
  224. * PURPOSE: Creates a thread to monitor user processes
  225. *
  226. \***************************************************************************/
  227. LPVOID
  228. StartUserProcessMonitor(
  229. //HWND hwndNotify
  230. )
  231. {
  232. PUSER_PROCESS_MONITOR pMonitor;
  233. DWORD ThreadId;
  234. pMonitor = LocalAlloc(LPTR, sizeof(USER_PROCESS_MONITOR));
  235. if (pMonitor == NULL) {
  236. return(NULL);
  237. }
  238. //
  239. // Initialize monitor fields
  240. //
  241. //pMonitor->hwndNotify = hwndNotify;
  242. pMonitor->TerminateEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
  243. //
  244. // Create the monitor thread
  245. //
  246. pMonitor->Thread = CreateThread(
  247. NULL, // Use default ACL
  248. 0, // Same stack size
  249. UserProcessMonitorThread, // Start address
  250. (LPVOID)pMonitor, // Parameter
  251. 0, // Creation flags
  252. &ThreadId // Get the id back here
  253. );
  254. if (pMonitor->Thread == NULL) {
  255. DebugLog((DEB_ERROR, "Failed to create monitor thread, error = %d", GetLastError()));
  256. LocalFree(pMonitor);
  257. return(NULL);
  258. }
  259. return((LPVOID)pMonitor);
  260. }
  261. VOID
  262. DeleteUserProcessMonitor(
  263. LPVOID Parameter
  264. )
  265. {
  266. PUSER_PROCESS_MONITOR pMonitor = (PUSER_PROCESS_MONITOR)Parameter;
  267. BOOL Result;
  268. if (!pMonitor)
  269. return;
  270. // Set the terminate event for this monitor
  271. // and wait for the monitor thread to exit
  272. SetEvent( pMonitor->TerminateEvent );
  273. if ( WaitForSingleObject( pMonitor->Thread, 5000 ) == WAIT_TIMEOUT )
  274. (VOID)TerminateThread(pMonitor->Thread, ERROR_SUCCESS);
  275. //
  276. // Close our handle to the monitor thread
  277. //
  278. Result = CloseHandle(pMonitor->Thread);
  279. if (!Result) {
  280. DebugLog((DEB_ERROR, "DeleteUserProcessMonitor: failed to close monitor thread, error = %d\n", GetLastError()));
  281. }
  282. //
  283. // Delete monitor object
  284. //
  285. CloseHandle(pMonitor->TerminateEvent);
  286. LocalFree(pMonitor);
  287. }
  288. HANDLE
  289. OpenUserProcessHandle()
  290. {
  291. HANDLE ProcessHandle = NULL; // handle to notify process
  292. int rc;
  293. //WCHAR UserName[USERNAME_LENGTH];
  294. ULONG SessionId;
  295. //PSID pUserSid;
  296. PSYSTEM_PROCESS_INFORMATION ProcessInfo;
  297. SYSTEM_SESSION_PROCESS_INFORMATION SessionProcessInfo;
  298. PSYSTEM_THREAD_INFORMATION ThreadInfo;
  299. PBYTE pBuffer;
  300. ULONG ByteCount;
  301. NTSTATUS status;
  302. ULONG MaxLen;
  303. ULONG TotalOffset;
  304. BOOL RetryIfNoneFound;
  305. ULONG retlen = 0;
  306. ByteCount = BUFFER_SIZE;
  307. Retry:
  308. RetryIfNoneFound = FALSE;
  309. SessionProcessInfo.SessionId = g_SessionId;
  310. for(;;) {
  311. if ( (pBuffer = LocalAlloc(LPTR, ByteCount )) == NULL ) {
  312. return(NULL);
  313. }
  314. SessionProcessInfo.Buffer = pBuffer;
  315. SessionProcessInfo.SizeOfBuf = ByteCount;
  316. /*
  317. * get process info
  318. */
  319. status = NtQuerySystemInformation(
  320. SystemSessionProcessInformation,
  321. &SessionProcessInfo,
  322. sizeof(SessionProcessInfo),
  323. &retlen );
  324. if ( NT_SUCCESS(status) )
  325. break;
  326. /*
  327. * Make sure buffer is big enough
  328. */
  329. if ( status != STATUS_INFO_LENGTH_MISMATCH ) {
  330. LocalFree ( pBuffer );
  331. return(NULL);
  332. }
  333. LocalFree( pBuffer );
  334. ByteCount *= 2;
  335. }
  336. if (retlen == 0) {
  337. LocalFree(pBuffer);
  338. return NULL;
  339. }
  340. /*
  341. * Loop through all processes. Find first process running on this station
  342. */
  343. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)pBuffer;
  344. TotalOffset = 0;
  345. rc = 0;
  346. for(;;) {
  347. ThreadInfo = (PSYSTEM_THREAD_INFORMATION)(ProcessInfo + 1);
  348. // SessionId = ProcessInfo->SessionId;
  349. // if (SessionId == g_SessionId) {
  350. /*
  351. * Get the User name for the SID of the process.
  352. */
  353. MaxLen = USERNAME_LENGTH;
  354. //LookupSidUser( pUserSid, UserName, &MaxLen);
  355. ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE,
  356. (DWORD)(UINT_PTR)ProcessInfo->UniqueProcessId);
  357. //
  358. // OpenProcess may fail for System processes like csrss.exe if we do not have enough privilege
  359. // In that case, we just skip that process because we r not worried about System processes anyway
  360. //
  361. if (!ProcessHandle && (GetLastError() == ERROR_ACCESS_DENIED) ) {
  362. goto NextProcess;
  363. }
  364. if ( ProcessHandle && !IsSystemLUID(ProcessHandle) && !IsSystemProcess( ProcessInfo) &&
  365. (ThreadInfo->ThreadState != 4) ) {
  366. //
  367. // Open the process for the monitor thread.
  368. //
  369. break;
  370. }
  371. if (ProcessHandle) {
  372. CloseHandle(ProcessHandle);
  373. ProcessHandle = NULL;
  374. } else {
  375. //
  376. // When OpenProcess fails, it means the process is already
  377. // gone. This can happen if the list is sufficiently long.
  378. // If for example the process was userinit.exe, it may have
  379. // spawned progman and exited by the time we see the entry
  380. // for userinit. But, since this is a snapshot of the list
  381. // of processes, progman may not be in this snapshot. So,
  382. // if we don't find any processes in this list, we have to
  383. // get another snapshot to avoid prematurely logging off the
  384. // user.
  385. //
  386. RetryIfNoneFound = TRUE;
  387. }
  388. // }
  389. NextProcess:
  390. if( ProcessInfo->NextEntryOffset == 0 ) {
  391. break;
  392. }
  393. TotalOffset += ProcessInfo->NextEntryOffset;
  394. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&pBuffer[TotalOffset];
  395. }
  396. LocalFree( pBuffer );
  397. if (!ProcessHandle && RetryIfNoneFound) {
  398. Sleep(4000);
  399. goto Retry;
  400. }
  401. return(ProcessHandle);
  402. }
  403. BOOL IsSystemLUID(HANDLE ProcessId)
  404. {
  405. HANDLE TokenHandle;
  406. UCHAR TokenInformation[ sizeof( TOKEN_STATISTICS ) ];
  407. ULONG ReturnLength;
  408. LUID CurrentLUID = { 0, 0 };
  409. LUID SystemLUID = SYSTEM_LUID;
  410. NTSTATUS Status;
  411. Status = NtOpenProcessToken( ProcessId,
  412. TOKEN_QUERY,
  413. &TokenHandle );
  414. if ( !NT_SUCCESS( Status ) )
  415. return(TRUE);
  416. NtQueryInformationToken( TokenHandle, TokenStatistics, &TokenInformation,
  417. sizeof(TokenInformation), &ReturnLength );
  418. NtClose( TokenHandle );
  419. RtlCopyLuid(&CurrentLUID,
  420. &(((PTOKEN_STATISTICS)TokenInformation)->AuthenticationId));
  421. if (RtlEqualLuid(&CurrentLUID, &SystemLUID)) {
  422. return(TRUE);
  423. } else {
  424. return(FALSE );
  425. }
  426. }
  427. /******************************************************************************
  428. *
  429. * IsSystemProcess
  430. *
  431. * Return whether the given process described by SYSTEM_PROCESS_INFORMATION
  432. * is an NT "system" process, and not a user program.
  433. *
  434. * ENTRY:
  435. * pProcessInfo (input)
  436. * Pointer to an NT SYSTEM_PROCESS_INFORMATION structure for a single
  437. * process.
  438. * EXIT:
  439. * TRUE if this is an NT system process; FALSE if a general user process.
  440. *
  441. *****************************************************************************/
  442. BOOLEAN
  443. IsSystemProcess( PSYSTEM_PROCESS_INFORMATION pSysProcessInfo)
  444. {
  445. DWORD dwIndex;
  446. WCHAR *WellKnownSysProcTable[] = {
  447. L"csrss.exe",
  448. L"smss.exe",
  449. L"screg.exe",
  450. L"lsass.exe",
  451. L"spoolss.exe",
  452. L"EventLog.exe",
  453. L"netdde.exe",
  454. L"clipsrv.exe",
  455. L"lmsvcs.exe",
  456. L"MsgSvc.exe",
  457. L"winlogon.exe",
  458. L"NETSTRS.EXE",
  459. L"nddeagnt.exe",
  460. L"os2srv.exe",
  461. L"wfshell.exe",
  462. L"win.com",
  463. L"rdpclip.exe",
  464. L"conime.exe",
  465. L"proquota.exe",
  466. L"imepadsv.exe",
  467. L"ctfmon.exe",
  468. NULL
  469. };
  470. if (dwNumberofSysProcs == 0)
  471. {
  472. /*
  473. * we failed to read the sys processes from registry. so lets fall back to our well known proc list.
  474. */
  475. for( dwIndex=0; WellKnownSysProcTable[dwIndex]; dwIndex++) {
  476. if ( !_wcsnicmp( pSysProcessInfo->ImageName.Buffer,
  477. WellKnownSysProcTable[dwIndex],
  478. pSysProcessInfo->ImageName.Length) ) {
  479. return(TRUE);
  480. }
  481. }
  482. }
  483. else
  484. {
  485. /*
  486. * Compare its image name against some well known system image names.
  487. */
  488. for( dwIndex=0; dwIndex < dwNumberofSysProcs; dwIndex++) {
  489. if ( !_wcsnicmp( pSysProcessInfo->ImageName.Buffer,
  490. SysProcTable[dwIndex],
  491. pSysProcessInfo->ImageName.Length) ) {
  492. return(TRUE);
  493. }
  494. }
  495. }
  496. return(FALSE);
  497. } /* IsSystemProcess() */