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.

516 lines
14 KiB

  1. // Copyright (c) 1998-1999 Microsoft Corporation
  2. /******************************************************************************
  3. *
  4. * PROCUTIL.C
  5. *
  6. * Various useful utilities for dealing with processes
  7. * that are useful across a range of utilities and apps.
  8. *
  9. *
  10. *******************************************************************************/
  11. #include <nt.h>
  12. #include <ntrtl.h>
  13. #include <nturtl.h>
  14. #include <windows.h>
  15. #include <ntddkbd.h>
  16. #include <ntddmou.h>
  17. #include <assert.h>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <time.h>
  21. #include <process.h>
  22. #include <string.h>
  23. #include <malloc.h>
  24. #include <winstaw.h>
  25. #include <utilsub.h>
  26. // from helpers.c
  27. BOOL GetResourceStringFromUtilDll(UINT uID, LPTSTR szBuffer, int iBufferSize);
  28. void ErrorOutFromResource(UINT uiStringResource, ...);
  29. #include "utilsubres.h" // resources refrenced in this file.
  30. /*
  31. * Local function prototypes.
  32. */
  33. VOID LookupSidUser( PSID pSid, PWCHAR pUserName, PULONG pcbUserName );
  34. /*
  35. * RefreshProcessObjectCaches()
  36. *
  37. * Refresh (invalidate) any caches that may be used by process object
  38. * utilities.
  39. *
  40. * This is currently a place holder, but is here so that utilities can call
  41. * it, thus being isolated from any future decisions to add caching.
  42. */
  43. VOID WINAPI
  44. RefreshProcessObjectCaches()
  45. {
  46. RefreshUserSidCrcCache();
  47. }
  48. /******************************************************************************
  49. *
  50. * ProcessObjectMatch
  51. *
  52. * General Name match function against a process.
  53. *
  54. * The multi-user admin utilities can take a user name, winstation name,
  55. * a winstation id, or process id as an argument to a command that targets
  56. * a process for some action (query status, kill, etc.)
  57. *
  58. * This function does general compares of the supplied name to see if it
  59. * applies to the given process because the name represents the NT user
  60. * account, a winstations system name, the winstations unique id, or the
  61. * processes unique id.
  62. *
  63. * The various information about a process is supplied by the caller. Because
  64. * of the way processes are enumerated from the NT system, it is easier
  65. * and faster for the caller to supply this information than for the routine
  66. * to retrieve it itself. This could be folded into a general EnumerateProcess()
  67. * if needed. Currently this routine serves the purpose of having one unified
  68. * way of handling process objects across all utilities.
  69. *
  70. *
  71. * Matching:
  72. *
  73. * An integer number is assumed to be an NT process ID unless NumberIsLogonId
  74. * is set, which then says to treat it as a LogonId.
  75. *
  76. * A name starting with a character is tested first as a winstation name, then
  77. * as a user name, finally as a program image name. A user or group name
  78. * could stand alone, or be preceded by a '\' to be [somewhat] compatible
  79. * with the OS/2 product.
  80. *
  81. * Parameters:
  82. *
  83. * Pid (input)
  84. * Windows NT unique process identifier
  85. * LogonId (input)
  86. * Logon (also called Session) ID the process is executing on.
  87. * NumberIsLogonId (input)
  88. * Treat a number in pMatchName as a LogonId not an PID number.
  89. * pMatchName (input)
  90. * Name for match testing
  91. * pWinStationName (input)
  92. * Name of WinStation for process.
  93. * pUserName (input)
  94. * Name of User for process.
  95. * pImageName (input)
  96. * Image name of executing program for process.
  97. *
  98. *****************************************************************************/
  99. BOOLEAN WINAPI
  100. ProcessObjectMatch( HANDLE Pid,
  101. ULONG LogonId,
  102. int NumberIsLogonId,
  103. PWCHAR pMatchName,
  104. PWCHAR pWinStationName,
  105. PWCHAR pUserName,
  106. PWCHAR pImageName )
  107. {
  108. ULONG tmp;
  109. HANDLE htmp;
  110. /*
  111. * Check for wild card
  112. */
  113. if( pMatchName[0] == L'*' ) return( TRUE );
  114. /*
  115. * If someone puts a '\' in front of pMatchName, strip it off
  116. */
  117. if( pMatchName[0] == L'\\' ) pMatchName++;
  118. /*
  119. * First, if the match name is a number, check for == to process ID or
  120. * LogonId.
  121. */
  122. if( iswdigit( pMatchName[0] ) ) {
  123. tmp = wcstol( pMatchName, NULL, 10 );
  124. htmp = LongToPtr (tmp);
  125. if( NumberIsLogonId && (tmp == LogonId) )
  126. return( TRUE );
  127. else if( htmp == Pid )
  128. return( TRUE );
  129. else
  130. return( FALSE );
  131. }
  132. /*
  133. * Then, check the match name against the WinStation Name of the process.
  134. */
  135. if ( !_wcsicmp( pWinStationName, pMatchName ) ) {
  136. return( TRUE );
  137. }
  138. /*
  139. * Then, check the match name against the UserName of the process.
  140. */
  141. if( !_wcsicmp( pUserName, pMatchName ) ) {
  142. return( TRUE );
  143. }
  144. /*
  145. * Finally, check the match name against the image name of the process.
  146. */
  147. if( !_wcsicmp( pImageName, pMatchName ) ) {
  148. return(TRUE);
  149. }
  150. return( FALSE );
  151. }
  152. /*
  153. * This is the cache maintained by the GetUserNameFromSid function
  154. *
  155. * It is thread safe through the use of ULock.
  156. */
  157. typedef struct TAGUSERSIDLIST {
  158. struct TAGUSERSIDLIST *Next;
  159. USHORT SidCrc;
  160. WCHAR UserName[USERNAME_LENGTH];
  161. } USERSIDLIST, *PUSERSIDLIST;
  162. static PUSERSIDLIST pUList = NULL;
  163. static RTL_CRITICAL_SECTION ULock;
  164. static BOOLEAN ULockInited = FALSE;
  165. /***************************************************************************
  166. *
  167. * InitULock
  168. *
  169. * Since we do not require the user to call an initialize function,
  170. * we must initialize our critical section in a thread safe manner.
  171. *
  172. * The problem is, a critical section is needed to guard against multiple
  173. * threads trying to init the critical section at the same time.
  174. *
  175. * The solution that Nt uses, in which RtlInitializeCriticalSection itself
  176. * uses, is to wait on a kernel supported process wide Mutant before proceding.
  177. * This Mutant almost works by itself, but RtlInitializeCriticalSection does
  178. * not wait on it until after trashing the semaphore count. So we wait on
  179. * it ourselves, since it can be acquired recursively.
  180. *
  181. ***************************************************************************/
  182. NTSTATUS InitULock()
  183. {
  184. NTSTATUS status = STATUS_SUCCESS;
  185. RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
  186. /*
  187. * Make sure another thread did not beat us here
  188. */
  189. if( ULockInited == FALSE ){
  190. status = RtlInitializeCriticalSection( &ULock );
  191. if (status == STATUS_SUCCESS) {
  192. ULockInited = TRUE;
  193. }
  194. }
  195. RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
  196. return status;
  197. }
  198. /***************************************************************************
  199. *
  200. * RefreshUserSidCrcCache
  201. *
  202. * Invalidate the User/SidCrc cache so that the newest information
  203. * will be fetched from the system.
  204. *
  205. ***************************************************************************/
  206. VOID WINAPI
  207. RefreshUserSidCrcCache( )
  208. {
  209. NTSTATUS status = STATUS_SUCCESS;
  210. PUSERSIDLIST pEntry, pNext;
  211. if( pUList == NULL ) return;
  212. /*
  213. * Make sure critical section has been inited
  214. */
  215. if( !ULockInited ) {
  216. status = InitULock();
  217. }
  218. if (status == STATUS_SUCCESS) {
  219. RtlEnterCriticalSection( &ULock );
  220. pEntry = pUList;
  221. while( pEntry ) {
  222. pNext = pEntry->Next;
  223. free( pEntry );
  224. pEntry = pNext;
  225. }
  226. pUList = NULL;
  227. RtlLeaveCriticalSection( &ULock );
  228. }
  229. }
  230. /******************************************************************************
  231. *
  232. * GetUserNameFromSid
  233. *
  234. * Attempts to retrieve the user (login) name of the process by first looking
  235. * in our User/SidCrc cache table, then (if no match) looking up the SID in
  236. * the SAM database and adding the new entry to the User/SidCrc table.
  237. *
  238. * Input
  239. *
  240. * IN pUserSid Sid pointer
  241. *
  242. * OUT NameBuf WCHAR pointer to buffer for name
  243. *
  244. * IN/OUT pBufSize PULONG NameBuf size
  245. *
  246. * Will always return a user name, which will be "(unknown)" if the SID is
  247. * invalid or can't determine the user/SID relationship for any other reason.
  248. *
  249. *****************************************************************************/
  250. VOID WINAPI
  251. GetUserNameFromSid( PSID pUserSid, PWCHAR pBuffer, PULONG pcbBuffer )
  252. {
  253. NTSTATUS status = STATUS_SUCCESS;
  254. USHORT SidCrc = 0;
  255. PUSERSIDLIST pEntry;
  256. /*
  257. * Make sure critical section has been inited
  258. */
  259. if( !ULockInited ) {
  260. status = InitULock();
  261. }
  262. /*
  263. * Determine SID length in bytes and calculate a 16-bit CRC for it,
  264. * to facilitate quick matching.
  265. */
  266. if ( pUserSid )
  267. SidCrc = CalculateCrc16( (PBYTE)pUserSid,
  268. (USHORT)GetLengthSid(pUserSid) );
  269. /*
  270. * First: Before performing the expensive LookupAccountSid() function,
  271. * see if we've encountered this SID already, and match the user name
  272. * if so.
  273. */
  274. if ( status == STATUS_SUCCESS && pUList ) {
  275. RtlEnterCriticalSection( &ULock );
  276. pEntry = pUList;
  277. while( pEntry ) {
  278. if ( SidCrc == pEntry->SidCrc ) {
  279. wcsncpy( pBuffer, pEntry->UserName, (*pcbBuffer)-1 );
  280. pBuffer[(*pcbBuffer)-1] = 0;
  281. *pcbBuffer = wcslen(pBuffer);
  282. RtlLeaveCriticalSection( &ULock );
  283. return;
  284. }
  285. pEntry = pEntry->Next;
  286. }
  287. RtlLeaveCriticalSection( &ULock );
  288. }
  289. /*
  290. * Last resort: Determine the user name associated with the SID using
  291. * the LookupAccountSid() API, embedded in our local function
  292. * LookupSidUser().
  293. */
  294. LookupSidUser( pUserSid, pBuffer, pcbBuffer );
  295. /*
  296. * Add this new User/Sid relationship in our User/Sid cache list.
  297. */
  298. if (status == STATUS_SUCCESS) {
  299. RtlEnterCriticalSection( &ULock );
  300. if ( (pEntry = (PUSERSIDLIST)malloc(sizeof(USERSIDLIST))) ) {
  301. pEntry->SidCrc = SidCrc;
  302. wcsncpy( pEntry->UserName, pBuffer, USERNAME_LENGTH - 1 );
  303. pEntry->UserName[USERNAME_LENGTH-1] = 0;
  304. pEntry->Next = pUList;
  305. pUList = pEntry;
  306. }
  307. RtlLeaveCriticalSection( &ULock );
  308. }
  309. }
  310. /******************************************************************************
  311. * LookupSidUser
  312. *
  313. * Fetch the user name associated with the specified SID.
  314. *
  315. * ENTRY:
  316. * pSid (input)
  317. * Points to SID to match to user name.
  318. * pUserName (output)
  319. * Points to buffer to place the user name into.
  320. * pcbUserName (input/output)
  321. * Specifies the size in bytes of the user name buffer. The returned
  322. * user name will be truncated to fit this buffer (including NUL
  323. * terminator) if necessary and this variable set to the number of
  324. * characters copied to pUserName.
  325. *
  326. * EXIT:
  327. *
  328. * LookupSidUser() will always return a user name. If the specified
  329. * SID fails to match to a user name, then the user name "(unknown)" will
  330. * be returned.
  331. *
  332. *****************************************************************************/
  333. VOID
  334. LookupSidUser( PSID pSid,
  335. PWCHAR pUserName,
  336. PULONG pcbUserName )
  337. {
  338. WCHAR DomainBuffer[DOMAIN_LENGTH], UserBuffer[USERNAME_LENGTH];
  339. DWORD cbDomainBuffer=sizeof(DomainBuffer), cbUserBuffer=sizeof(UserBuffer),
  340. Error;
  341. PWCHAR pDomainBuffer = NULL, pUserBuffer = NULL;
  342. SID_NAME_USE SidNameUse;
  343. /*
  344. * Fetch user name from SID: try user lookup with a reasonable Domain and
  345. * Sid buffer size first, before resorting to alloc.
  346. */
  347. if ( !LookupAccountSid( NULL, pSid,
  348. UserBuffer, &cbUserBuffer,
  349. DomainBuffer, &cbDomainBuffer, &SidNameUse ) ) {
  350. if ( ((Error = GetLastError()) == ERROR_INSUFFICIENT_BUFFER) ) {
  351. if ( cbDomainBuffer > sizeof(DomainBuffer) ) {
  352. if ( !(pDomainBuffer =
  353. (PWCHAR)malloc(
  354. cbDomainBuffer * sizeof(WCHAR))) ) {
  355. Error = ERROR_NOT_ENOUGH_MEMORY;
  356. goto BadDomainAlloc;
  357. }
  358. }
  359. if ( cbUserBuffer > sizeof(UserBuffer) ) {
  360. if ( !(pUserBuffer =
  361. (PWCHAR)malloc(
  362. cbUserBuffer * sizeof(WCHAR))) ) {
  363. Error = ERROR_NOT_ENOUGH_MEMORY;
  364. goto BadUserAlloc;
  365. }
  366. }
  367. if ( !LookupAccountSid( NULL, pSid,
  368. pUserBuffer ?
  369. pUserBuffer : UserBuffer,
  370. &cbUserBuffer,
  371. pDomainBuffer ?
  372. pDomainBuffer : DomainBuffer,
  373. &cbDomainBuffer,
  374. &SidNameUse ) ) {
  375. Error = GetLastError();
  376. goto BadLookup;
  377. }
  378. } else {
  379. goto BadLookup;
  380. }
  381. }
  382. /*
  383. * Copy the user name into the specified buffer, truncating if necessary.
  384. */
  385. wcsncpy( pUserName, pUserBuffer ? pUserBuffer : UserBuffer,
  386. (*pcbUserName)-1 );
  387. pUserName[(*pcbUserName)-1] = 0;
  388. *pcbUserName = wcslen(pUserName);
  389. /*
  390. * Free our allocs (if any) and return.
  391. */
  392. if ( pDomainBuffer )
  393. free(pDomainBuffer);
  394. if ( pUserBuffer )
  395. free(pUserBuffer);
  396. return;
  397. /*--------------------------------------
  398. * Error clean-up and return...
  399. */
  400. BadLookup:
  401. BadUserAlloc:
  402. BadDomainAlloc:
  403. if ( pDomainBuffer )
  404. free(pDomainBuffer);
  405. if ( pUserBuffer )
  406. free(pUserBuffer);
  407. GetResourceStringFromUtilDll(IDS_UNKNOWN_USERNAME, pUserName, (*pcbUserName)-1);
  408. pUserName[(*pcbUserName)-1] = 0;
  409. *pcbUserName = wcslen(pUserName);
  410. return;
  411. }
  412. /*******************************************************************************
  413. *
  414. * AreWeRunningTerminalServices
  415. *
  416. * Check if we are running terminal server
  417. *
  418. * ENTRY:
  419. *
  420. * EXIT: BOOL: True if we are running Terminal Services False if we
  421. * are not running Terminal Services
  422. *
  423. *
  424. ******************************************************************************/
  425. BOOL AreWeRunningTerminalServices(void)
  426. {
  427. OSVERSIONINFOEX osVersionInfo;
  428. DWORDLONG dwlConditionMask = 0;
  429. ZeroMemory(&osVersionInfo, sizeof(OSVERSIONINFOEX));
  430. osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  431. osVersionInfo.wSuiteMask = VER_SUITE_TERMINAL | VER_SUITE_SINGLEUSERTS;
  432. VER_SET_CONDITION( dwlConditionMask, VER_SUITENAME, VER_OR );
  433. return VerifyVersionInfo(
  434. &osVersionInfo,
  435. VER_SUITENAME,
  436. dwlConditionMask
  437. );
  438. }