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.

524 lines
14 KiB

  1. /******************************************************************************
  2. * WSTUTIL.C
  3. *
  4. * Various useful utilities for dealing with multi-user WinStations and User
  5. * accounts under CITRIX NT Multi-user that are useful across a range of
  6. * utilities and apps.
  7. *
  8. * Copyright Citrix Systems Inc. 1993
  9. * Copyright (C) 1997-1999 Microsoft Corp.
  10. *
  11. * Author: John Richardson
  12. *******************************************************************************/
  13. #include <nt.h>
  14. #include <ntrtl.h>
  15. #include <nturtl.h>
  16. #include <windows.h>
  17. #include <ntddkbd.h>
  18. #include <ntddmou.h>
  19. #include <assert.h>
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <time.h>
  23. #include <process.h>
  24. #include <string.h>
  25. #include <malloc.h>
  26. #include <winstaw.h>
  27. #include <utilsub.h>
  28. /*
  29. * RefreshAllCaches
  30. *
  31. * Invalidate any caches maintained by the UTILSUB.DLL
  32. *
  33. * This does not need to be called for utilities that exit when done, but
  34. * are for server, or monitoring type programs that need to periodicly
  35. * see the latest system information.
  36. * IE: A new user could have logged onto a given winstation since the last
  37. * call.
  38. *
  39. *
  40. * Exit
  41. *
  42. * Any caches in the UTILSUB.DLL have been invalidated insuring fresh
  43. * system information on future calls.
  44. *
  45. */
  46. VOID WINAPI
  47. RefreshAllCaches()
  48. {
  49. RefreshWinStationCaches();
  50. RefreshProcessObjectCaches();
  51. }
  52. /*
  53. * RefreshWinStationCaches
  54. *
  55. * Invalidate any caches maintained by the WinStation helper utilities.
  56. *
  57. * This does not need to be called for utilities that exit when done, but
  58. * are for server, or monitoring type programs that need to periodicly
  59. * see the latest system information.
  60. * IE: A new user could have logged onto a given winstation since the last
  61. * call.
  62. *
  63. *
  64. * Exit
  65. *
  66. * Makes sure that any WinStation helper utility calls will return the
  67. * system information at least up to date as the time that this call
  68. * was made.
  69. *
  70. */
  71. VOID WINAPI
  72. RefreshWinStationCaches()
  73. {
  74. RefreshWinStationObjectCache();
  75. RefreshWinStationNameCache();
  76. }
  77. /*
  78. * GetCurrentLogonId
  79. *
  80. * Gets the WinStation ID for the current processes WinStation
  81. *
  82. * Exit
  83. *
  84. * ID of the current processes WinStation
  85. *
  86. */
  87. ULONG WINAPI
  88. GetCurrentLogonId()
  89. {
  90. return( NtCurrentPeb()->SessionId );
  91. }
  92. /*
  93. * GetCurrentWinStationName
  94. *
  95. * Get the current UNICODE name for the WinStation for this process
  96. *
  97. * Input:
  98. *
  99. * pName - Pointer to wide character buffer for name
  100. *
  101. * MaxSize - Maximum number of characters in buffer (including terminator).
  102. *
  103. * pName - Pointer to wide character buffer for name
  104. *
  105. * Output:
  106. *
  107. */
  108. VOID WINAPI
  109. GetCurrentWinStationName( PWCHAR pName, int MaxSize )
  110. {
  111. GetWinStationNameFromId( NtCurrentPeb()->SessionId, pName, MaxSize );
  112. }
  113. /*
  114. * This is the cache maintained by the GetWinStationNameFromId function
  115. *
  116. * It is thread safe through the use of WLock.
  117. */
  118. typedef struct TAGWINSTATIONLIST {
  119. struct TAGWINSTATIONLIST *Next;
  120. LOGONID LogonId;
  121. } WINSTATIONLIST, *PWINSTATIONLIST;
  122. static PWINSTATIONLIST pWList = NULL;
  123. static RTL_CRITICAL_SECTION WLock;
  124. static BOOLEAN WLockInited = FALSE;
  125. /***************************************************************************
  126. *
  127. * InitWLock
  128. *
  129. * Since we do not require the user to call an initialize function,
  130. * we must initialize our critical section in a thread safe manner.
  131. *
  132. * The problem is, a critical section is needed to guard against multiple
  133. * threads trying to init the critical section at the same time.
  134. *
  135. * The solution that Nt uses, in which RtlInitializeCriticalSection itself
  136. * uses, is to wait on a kernel supported process wide Mutant before proceding.
  137. * This Mutant almost works by itself, but RtlInitializeCriticalSection does
  138. * not wait on it until after trashing the semaphore count. So we wait on
  139. * it ourselves, since it can be acquired recursively.
  140. *
  141. ***************************************************************************/
  142. NTSTATUS InitWLock()
  143. {
  144. NTSTATUS status = STATUS_SUCCESS;
  145. RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
  146. /*
  147. * Make sure another thread did not beat us here
  148. */
  149. if( WLockInited == FALSE ){
  150. status = RtlInitializeCriticalSection( &WLock );
  151. if (status == STATUS_SUCCESS) {
  152. WLockInited = TRUE;
  153. }
  154. }
  155. RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
  156. return status;
  157. }
  158. /***************************************************************************
  159. *
  160. * RefreshWinStationNameCache
  161. *
  162. * Invalidate the WinStation Name cache so that the newest information
  163. * will be fetched from the system.
  164. *
  165. ***************************************************************************/
  166. VOID WINAPI
  167. RefreshWinStationNameCache( )
  168. {
  169. NTSTATUS status = STATUS_SUCCESS;
  170. PWINSTATIONLIST pEntry, pNext;
  171. if( pWList == NULL ) return;
  172. /*
  173. * Make sure critical section has been inited
  174. */
  175. if( !WLockInited ) {
  176. status = InitWLock();
  177. }
  178. if (status == STATUS_SUCCESS) {
  179. RtlEnterCriticalSection( &WLock );
  180. pEntry = pWList;
  181. while( pEntry ) {
  182. pNext = pEntry->Next;
  183. free( pEntry );
  184. pEntry = pNext;
  185. }
  186. pWList = NULL;
  187. RtlLeaveCriticalSection( &WLock );
  188. }
  189. }
  190. /*
  191. * GetWinStationNameFromId
  192. *
  193. * Attempt to retrieve the WinStationName for the given LogonId.
  194. *
  195. * Parameters:
  196. *
  197. * LogonId (input)
  198. * Unique LogonId
  199. *
  200. * pName (output)
  201. * Pointer to buffer for name
  202. *
  203. * MaxSize (input)
  204. * Maximum number of characters in buffer (including terminator).
  205. *
  206. * Returns
  207. * TRUE if name was retreived, FALSE otherwise.
  208. *
  209. */
  210. BOOLEAN WINAPI
  211. GetWinStationNameFromId( ULONG LogonId, PWCHAR pName, int MaxSize )
  212. {
  213. NTSTATUS status = STATUS_SUCCESS;
  214. PLOGONID pIdBase, pId;
  215. int rc;
  216. ULONG Count;
  217. PWINSTATIONLIST pEntryBase, pEntry;
  218. // Since We do not have a WinStationNamefromId Sm Api like we do for
  219. // LogonIdfromName, we will perform a WinStationEnumerate function across
  220. // all WinStations known by the Session Manager, and store them in a locally
  221. // maintained list. We do this so we that this search against the session
  222. // manager is not done every time we're called.
  223. //
  224. // Another alternative that was tested is to open the WinStation itself
  225. // and then do a WinStationQueryInformation against it in order to
  226. // retrieve its name from itself. This is much slower because we must
  227. // set up and tear down an LPC connection to each WinStation, as opposed
  228. // to the one connection we get to the session manager.
  229. /*
  230. * Make sure critical section has been inited
  231. */
  232. if( !WLockInited ) {
  233. status = InitWLock();
  234. }
  235. if (status == STATUS_SUCCESS) {
  236. RtlEnterCriticalSection( &WLock );
  237. // Initialize the list the first time
  238. if( pWList == NULL ) {
  239. rc = WinStationEnumerate( SERVERNAME_CURRENT, &pIdBase, &Count );
  240. if( rc ) {
  241. /*
  242. * Allocate an Entry for each enumerated winstation.
  243. */
  244. pEntryBase = (PWINSTATIONLIST)malloc( Count * sizeof(WINSTATIONLIST) );
  245. if( pEntryBase == NULL ) {
  246. pWList = NULL; // We are having severe problems
  247. SetLastError( ERROR_NOT_ENOUGH_MEMORY );
  248. WinStationFreeMemory(pIdBase);
  249. RtlLeaveCriticalSection( &WLock );
  250. return( FALSE );
  251. }
  252. /*
  253. * Load up Entries.
  254. */
  255. for ( pEntry = pEntryBase, pId = pIdBase;
  256. Count ;
  257. Count--, pEntry++, pId++ ) {
  258. pEntry->LogonId = *pId;
  259. pEntry->Next = pWList;
  260. pWList = pEntry;
  261. }
  262. /*
  263. * Free enumerate buffer.
  264. */
  265. WinStationFreeMemory(pIdBase);
  266. }
  267. } // End if pWList == NULL
  268. pEntry = pWList;
  269. while ( pEntry ) {
  270. if( pEntry->LogonId.LogonId == LogonId ) {
  271. wcsncpy( pName, pEntry->LogonId.WinStationName, MaxSize-1 );
  272. pName[MaxSize-1] = 0;
  273. RtlLeaveCriticalSection( &WLock );
  274. return( TRUE );
  275. }
  276. pEntry = pEntry->Next;
  277. }
  278. RtlLeaveCriticalSection( &WLock );
  279. }
  280. // If we can not find its name, print its ID #
  281. wsprintf( pName, L"ID %d", LogonId );
  282. return( TRUE );
  283. }
  284. /*
  285. * GetCurrentUserName
  286. *
  287. * Get the current UNICODE name for the logon USER for this process
  288. *
  289. * Input:
  290. *
  291. * pName - Pointer to wide character buffer for name
  292. *
  293. * MaxSize - Maximum number of characters in buffer (including terminator)
  294. *
  295. *
  296. * Output:
  297. *
  298. */
  299. VOID WINAPI
  300. GetCurrentUserName( PWCHAR pName, int MaxSize )
  301. {
  302. /*
  303. * The quickest way appears to open the current processes WinStation and
  304. * get the name from it. The other way would be to open the process, then
  305. * its token, extract the SID, then lookup the SID in the SAM database.
  306. * We have conviently stored the user name in the WinStation at Logon
  307. * time, so we'll use that.
  308. */
  309. GetWinStationUserName( LOGONID_CURRENT, pName, MaxSize );
  310. return;
  311. }
  312. /*
  313. * GetWinStationUserName
  314. *
  315. * Get the UNICODE name for the USER for the winstation
  316. *
  317. * Input:
  318. *
  319. * LogonId - integer identifier for WinStation
  320. *
  321. * pName - Pointer to wide character buffer for name
  322. *
  323. * MaxSize - Maximum number of characters in buffer (including terminator)
  324. *
  325. *
  326. * Output:
  327. *
  328. */
  329. BOOLEAN WINAPI
  330. GetWinStationUserName( ULONG LogonId, PWCHAR pName, int MaxSize )
  331. {
  332. BOOLEAN rc;
  333. ULONG ReturnLength;
  334. WINSTATIONINFORMATION Info;
  335. if( MaxSize == 0) return( FALSE );
  336. memset( &Info, 0, sizeof(WINSTATIONINFORMATION) );
  337. rc = WinStationQueryInformation( SERVERNAME_CURRENT,
  338. LogonId,
  339. WinStationInformation,
  340. (PVOID)&Info,
  341. sizeof(WINSTATIONINFORMATION),
  342. &ReturnLength);
  343. if(!rc){
  344. pName[0] = 0;
  345. return( FALSE );
  346. }
  347. if(ReturnLength != sizeof(WINSTATIONINFORMATION)) {
  348. pName[0] = 0; // Version mismatch
  349. return( FALSE );
  350. }
  351. /*
  352. * Now copy the name out
  353. */
  354. if( MaxSize > USERNAME_LENGTH ) {
  355. MaxSize = USERNAME_LENGTH;
  356. }
  357. wcsncpy( pName, Info.UserName, MaxSize-1 );
  358. pName[MaxSize-1] = 0; // insure null termination if string is truncated
  359. return( TRUE );
  360. }
  361. /*
  362. * These variables maintain a one entry cache so that we
  363. * do not have to keep querying the winstation (causes an LPC)
  364. * each time called.
  365. */
  366. static ULONG CachedId = (ULONG)(-1);
  367. static WCHAR CachedUserName[USERNAME_LENGTH];
  368. /**************************************************************************
  369. *
  370. * RefreshWinStationObjectCache
  371. *
  372. * Flush the cache for the WinStationObject name comparision function.
  373. *
  374. **************************************************************************/
  375. VOID WINAPI
  376. RefreshWinStationObjectCache()
  377. {
  378. CachedId = (ULONG)(-1);
  379. CachedUserName[0] = 0;
  380. }
  381. /*
  382. * WinStationObjectMatch
  383. *
  384. * General Name match function against a WinStation.
  385. *
  386. * The CITRIX admin utilities can take a user name, winstation name, or
  387. * a winstation id as an argument to a command that targets a winstation
  388. * for some action (send a message, query status, reset, etc.)
  389. *
  390. * This function does general compares of the supplied name to see if it
  391. * applies to the given winstation because the name represents the logged
  392. * on user of the winstation, the winstations system name when attached, or
  393. * the winstations unique id. It replaces some of the function in MumProc()
  394. * on the CITRIX OS/2 product.
  395. *
  396. *
  397. * NOTE: The caching for this function assumes typical use of comparing this
  398. * winstation against a list of names across multiple calls.
  399. * It does not optimize for comparing one name at a time across all
  400. * winstation(s) in succession.
  401. *
  402. * Parameters:
  403. *
  404. * Id (input) WinStation Id for do the match against
  405. *
  406. * pName (input) UNICODE name for match testing
  407. */
  408. BOOLEAN WINAPI
  409. WinStationObjectMatch( PLOGONID Id, PWCHAR pName )
  410. {
  411. ULONG tmp;
  412. /*
  413. * Handle the wild card case
  414. */
  415. if( pName[0] == L'*' ) {
  416. return( TRUE );
  417. }
  418. /*
  419. * See if the supplied name is the name assigned to the WinStation
  420. */
  421. if( !_wcsnicmp( pName, Id->WinStationName, WINSTATIONNAME_LENGTH ) ) {
  422. return( TRUE );
  423. }
  424. /*
  425. * See if it represents the numerical id for the winstation
  426. */
  427. if( iswdigit( pName[0] ) ) {
  428. tmp = (ULONG)wcstol( pName, NULL, 10 );
  429. if( tmp == Id->LogonId ) {
  430. return( TRUE );
  431. }
  432. }
  433. /*
  434. * Else extract the logged on user name from the winstation itself
  435. * and compare this.
  436. */
  437. if( CachedId == Id->LogonId ) {
  438. if( !_wcsnicmp( CachedUserName, pName, USERNAME_LENGTH ) ) {
  439. return( TRUE );
  440. }
  441. }
  442. if ( Id->State == State_Down )
  443. return( FALSE );
  444. if( GetWinStationUserName( Id->LogonId, CachedUserName, USERNAME_LENGTH ) ) {
  445. CachedId = Id->LogonId;
  446. }
  447. else {
  448. CachedId = (ULONG)(-1); // In case name was trashed
  449. return( FALSE );
  450. }
  451. if( !_wcsnicmp( CachedUserName, pName, USERNAME_LENGTH ) ) {
  452. return( TRUE );
  453. }
  454. return( FALSE );
  455. }