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.

556 lines
15 KiB

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