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.

481 lines
14 KiB

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