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.

745 lines
19 KiB

  1. /*************************************************************************
  2. *
  3. * winsta.c
  4. *
  5. * System Library WinStation utilities
  6. *
  7. * This file contains common routines needed in many places in the
  8. * system. An example is that (3) separate DLL's in the spooler need
  9. * functions to deal with the current user of a WinStation. IE: Get
  10. * name, find which LogonId, get name by logonid, etc.
  11. *
  12. * This common library at least keeps the source management in one
  13. * place. This is likely to become another Hydra DLL's in the future
  14. * to reduce memory.
  15. *
  16. *
  17. *
  18. *
  19. *************************************************************************/
  20. #include <nt.h>
  21. #include <ntrtl.h>
  22. #include <nturtl.h>
  23. #include <windows.h>
  24. #include <stdio.h>
  25. #include "winsta.h"
  26. #include "syslib.h"
  27. #pragma warning (error:4312)
  28. #if DBG
  29. #define DBGPRINT(x) DbgPrint x
  30. #if DBGTRACE
  31. #define TRACE0(x) DbgPrint x
  32. #define TRACE1(x) DbgPrint x
  33. #else
  34. #define TRACE0(x)
  35. #define TRACE1(x)
  36. #endif
  37. #else
  38. #define DBGPRINT(x)
  39. #define TRACE0(x)
  40. #define TRACE1(x)
  41. #endif
  42. //
  43. // Structure for FindUserOnWinStation
  44. //
  45. typedef struct _FINDUSERDATA {
  46. LPWSTR pName;
  47. ULONG ResultLogonId;
  48. } FINDUSERDATA, *PFINDUSERDATA;
  49. /*****************************************************************************
  50. *
  51. * WinStationGetUserName
  52. *
  53. * Return the user name for the WinStation
  54. *
  55. * ENTRY:
  56. * Param1 (input/output)
  57. * Comments
  58. *
  59. * EXIT:
  60. * STATUS_SUCCESS - no error
  61. *
  62. ****************************************************************************/
  63. BOOL
  64. WinStationGetUserName(
  65. ULONG LogonId,
  66. PWCHAR pBuf,
  67. ULONG BufSize
  68. )
  69. {
  70. BOOL Result;
  71. ULONG ReturnLength;
  72. WINSTATIONINFORMATION WSInfo;
  73. memset( &WSInfo, 0, sizeof(WSInfo) );
  74. // Query it
  75. Result = WinStationQueryInformation(
  76. SERVERNAME_CURRENT,
  77. LogonId,
  78. WinStationInformation,
  79. &WSInfo,
  80. sizeof(WSInfo),
  81. &ReturnLength
  82. );
  83. if( !Result ) {
  84. DBGPRINT(("GetWinStationInfo: Error %d getting info on WinStation %d\n",GetLastError(),LogonId));
  85. return( FALSE );
  86. }
  87. // Scale BufSize to UNICODE characters
  88. if( BufSize >= sizeof(WCHAR) ) {
  89. BufSize /= sizeof(WCHAR);
  90. }
  91. else {
  92. BufSize = 0;
  93. }
  94. if( (BufSize > 1) && WSInfo.UserName[0] ) {
  95. wcsncpy( pBuf, WSInfo.UserName, BufSize );
  96. pBuf[BufSize-1] = (WCHAR)NULL;
  97. }
  98. else {
  99. pBuf[0] = (WCHAR)NULL;
  100. }
  101. return( TRUE );
  102. }
  103. /*****************************************************************************
  104. *
  105. * SearchUserCallback
  106. *
  107. * Callback for search function
  108. *
  109. * ENTRY:
  110. * Param1 (input/output)
  111. * Comments
  112. *
  113. * EXIT:
  114. * STATUS_SUCCESS - no error
  115. *
  116. ****************************************************************************/
  117. BOOLEAN
  118. SearchUserCallback(
  119. ULONG CurrentIndex,
  120. PLOGONIDW pInfo,
  121. ULONG_PTR lParam
  122. )
  123. {
  124. BOOL Result;
  125. PFINDUSERDATA p;
  126. WCHAR UserName[USERNAME_LENGTH+1];
  127. // Only active WinStations are valid
  128. if( pInfo->State != State_Active ) {
  129. // continue the search
  130. return( TRUE );
  131. }
  132. // Check the user on the WinStation
  133. Result = WinStationGetUserName( pInfo->LogonId, UserName, sizeof(UserName) );
  134. if( !Result ) {
  135. DBGPRINT(("SearchUserCallback: Error getting WinStation User Name LogonId %d\n",pInfo->LogonId,GetLastError()));
  136. // continue the search
  137. return( TRUE );
  138. }
  139. p = (PFINDUSERDATA)lParam;
  140. if( _wcsicmp(p->pName, UserName) == 0 ) {
  141. TRACE0(("SearchUserCallback: Found username %ws on WinStation LogonId %d\n",UserName,pInfo->LogonId));
  142. // Found it, return the LogonId
  143. p->ResultLogonId = pInfo->LogonId;
  144. // Stop the search
  145. return( FALSE );
  146. }
  147. // continue the search
  148. return( TRUE );
  149. }
  150. /*****************************************************************************
  151. *
  152. * FindUsersWinStation
  153. *
  154. * Find the given users WinStation.
  155. *
  156. * ENTRY:
  157. * Param1 (input/output)
  158. * Comments
  159. *
  160. * EXIT:
  161. * STATUS_SUCCESS - no error
  162. *
  163. ****************************************************************************/
  164. BOOL
  165. FindUsersWinStation(
  166. PWCHAR pName,
  167. PULONG pLogonId
  168. )
  169. {
  170. BOOL Result;
  171. FINDUSERDATA Data;
  172. ASSERT( pLogonId != NULL );
  173. // If a NULL name, we are not going to find it.
  174. if( (pName == NULL) ||
  175. (pName[0] == (WCHAR)NULL) ) {
  176. TRACE0(("FindUsersWinStation: NULL user name\n"));
  177. return( FALSE );
  178. }
  179. Data.ResultLogonId = (ULONG)(-1);
  180. Data.pName = pName;
  181. //
  182. // Use the WinStation Enumerator to check all the WinStations
  183. //
  184. Result = WinStationEnumeratorW(
  185. 0, // StartIndex
  186. SearchUserCallback, // enumerator callback function
  187. (ULONG_PTR)&Data // lParam is our structure
  188. );
  189. if( !Result ) {
  190. // Problem with enumerator
  191. DBGPRINT(("FindUsersWinStation: Problem with enumerator\n"));
  192. return(FALSE);
  193. }
  194. //
  195. // If ResultLogonId != (-1), a WinStation was found for the user
  196. //
  197. if( Data.ResultLogonId != (ULONG)(-1) ) {
  198. TRACE0(("FindUsersWinStation: Found LogonId %d\n",Data.ResultLogonId));
  199. *pLogonId = Data.ResultLogonId;
  200. return(TRUE);
  201. }
  202. TRACE0(("FindUsersWinStation: Could not find user %ws\n",pName));
  203. return(FALSE);
  204. }
  205. /*****************************************************************************
  206. *
  207. * WinStationGetIcaNameA
  208. *
  209. * ANSI version
  210. *
  211. * Get the ICA name from the supplied WinStations Logonid
  212. *
  213. * Returns it in newly allocated memory that must be freed with
  214. * RtlFreeHeap().
  215. *
  216. * ENTRY:
  217. * Param1 (input/output)
  218. * Comments
  219. *
  220. * EXIT:
  221. * STATUS_SUCCESS - no error
  222. *
  223. ****************************************************************************/
  224. PCHAR
  225. WinStationGetICANameA(
  226. ULONG LogonId
  227. )
  228. {
  229. BOOL Result;
  230. ULONG ReturnLength;
  231. PCHAR pName = NULL;
  232. WINSTATIONCLIENTA ClientInfo;
  233. WINSTATIONINFORMATIONA WSInfo;
  234. CHAR NameBuf[MAX_PATH+1];
  235. memset( &WSInfo, 0, sizeof(WSInfo) );
  236. Result = WinStationQueryInformationA(
  237. SERVERNAME_CURRENT,
  238. LogonId,
  239. WinStationInformation,
  240. &WSInfo,
  241. sizeof(WSInfo),
  242. &ReturnLength
  243. );
  244. if( !Result ) {
  245. DBGPRINT(("GetWinStationICANameA: Error %d getting info on WinStation\n",GetLastError()));
  246. return( NULL );
  247. }
  248. memset( &ClientInfo, 0, sizeof(ClientInfo) );
  249. // Query its Info
  250. Result = WinStationQueryInformationA(
  251. SERVERNAME_CURRENT,
  252. LogonId,
  253. WinStationClient,
  254. &ClientInfo,
  255. sizeof(ClientInfo),
  256. &ReturnLength
  257. );
  258. if( !Result ) {
  259. DBGPRINT(("GetWinStationICANameA: Error %d getting client info\n",GetLastError()));
  260. return( NULL );
  261. }
  262. //
  263. // If the ClientName is NULL, then we use the user
  264. // as the ICA name.
  265. //
  266. if( ClientInfo.ClientName[0] == (CHAR)NULL ) {
  267. #ifdef notdef // spec change...
  268. if( ClientInfo.SerialNumber )
  269. wsprintf( NameBuf, L"%ws-%d", WSInfo.UserName, ClientInfo.SerialNumber);
  270. else
  271. #endif
  272. sprintf( NameBuf, "%s", WSInfo.UserName);
  273. }
  274. else {
  275. // copy out the Client name
  276. strcpy( NameBuf, ClientInfo.ClientName );
  277. }
  278. ReturnLength = strlen( NameBuf ) + 1;
  279. pName = RtlAllocateHeap( RtlProcessHeap(), 0, ReturnLength );
  280. if( pName == NULL ) {
  281. return( NULL );
  282. }
  283. strcpy( pName, NameBuf );
  284. return( pName );
  285. }
  286. /*****************************************************************************
  287. *
  288. * WinStationGetIcaNameW
  289. *
  290. * UNICODE Version
  291. *
  292. * Get the ICA name from the supplied WinStations Logonid
  293. *
  294. * Returns it in newly allocated memory that must be freed with
  295. * RtlFreeHeap().
  296. *
  297. * ENTRY:
  298. * Param1 (input/output)
  299. * Comments
  300. *
  301. * EXIT:
  302. * STATUS_SUCCESS - no error
  303. *
  304. ****************************************************************************/
  305. PWCHAR
  306. WinStationGetICANameW(
  307. ULONG LogonId
  308. )
  309. {
  310. BOOL Result;
  311. ULONG ReturnLength;
  312. PWCHAR pName = NULL;
  313. WINSTATIONCLIENT ClientInfo;
  314. WINSTATIONINFORMATION WSInfo;
  315. WCHAR NameBuf[MAX_PATH+1];
  316. memset( &WSInfo, 0, sizeof(WSInfo) );
  317. Result = WinStationQueryInformationW(
  318. SERVERNAME_CURRENT,
  319. LogonId,
  320. WinStationInformation,
  321. &WSInfo,
  322. sizeof(WSInfo),
  323. &ReturnLength
  324. );
  325. if( !Result ) {
  326. DBGPRINT(("GetWinStationICANameW: Error %d getting info on WinStation\n",GetLastError()));
  327. return( NULL );
  328. }
  329. memset( &ClientInfo, 0, sizeof(ClientInfo) );
  330. // Query its Info
  331. Result = WinStationQueryInformationW(
  332. SERVERNAME_CURRENT,
  333. LogonId,
  334. WinStationClient,
  335. &ClientInfo,
  336. sizeof(ClientInfo),
  337. &ReturnLength
  338. );
  339. if( !Result ) {
  340. DBGPRINT(("GetWinStationICANameW: Error %d getting client info\n",GetLastError()));
  341. return( NULL );
  342. }
  343. //
  344. // If the ClientName is NULL, then we use the user
  345. // as the ICA name.
  346. //
  347. if( ClientInfo.ClientName[0] == (WCHAR)NULL ) {
  348. #ifdef notdef // spec change...
  349. if( ClientInfo.SerialNumber )
  350. wsprintf( NameBuf, L"%ws-%d", WSInfo.UserName, ClientInfo.SerialNumber);
  351. else
  352. #endif
  353. wsprintf( NameBuf, L"%ws", WSInfo.UserName);
  354. }
  355. else {
  356. // copy out the Client name
  357. wcscpy( NameBuf, ClientInfo.ClientName );
  358. }
  359. ReturnLength = wcslen( NameBuf ) + 1;
  360. ReturnLength *= sizeof(WCHAR);
  361. pName = RtlAllocateHeap( RtlProcessHeap(), 0, ReturnLength );
  362. if( pName == NULL ) {
  363. return( NULL );
  364. }
  365. wcscpy( pName, NameBuf );
  366. return( pName );
  367. }
  368. /*****************************************************************************
  369. *
  370. * WinStationIsHardWire
  371. *
  372. * Returns whether the WinStation is hardwired. IE: No modem
  373. * or network. Like a dumb terminal.
  374. *
  375. * ENTRY:
  376. * Param1 (input/output)
  377. * Comments
  378. *
  379. * EXIT:
  380. * STATUS_SUCCESS - no error
  381. *
  382. ****************************************************************************/
  383. BOOLEAN
  384. WinStationIsHardWire(
  385. ULONG LogonId
  386. )
  387. {
  388. return( FALSE );
  389. }
  390. /*****************************************************************************
  391. *
  392. * GetWinStationUserToken
  393. *
  394. * Return the token for the user currently logged onto the WinStation
  395. *
  396. * ENTRY:
  397. * LogonId (input)
  398. * LogonId of WinStation
  399. *
  400. * pUserToken (output)
  401. * Variable to place the returned token handle if successfull.
  402. *
  403. * EXIT:
  404. * STATUS_SUCCESS - no error
  405. *
  406. ****************************************************************************/
  407. BOOL
  408. GetWinStationUserToken(
  409. ULONG LogonId,
  410. PHANDLE pUserToken
  411. )
  412. {
  413. BOOL Result;
  414. ULONG ReturnLength;
  415. NTSTATUS Status;
  416. OBJECT_ATTRIBUTES ObjA;
  417. HANDLE ImpersonationToken;
  418. WINSTATIONUSERTOKEN Info;
  419. SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
  420. //
  421. // This gets the token of the user logged onto the WinStation
  422. // if we are an admin caller.
  423. //
  424. // This is so that CSRSS can dup the handle to our process
  425. Info.ProcessId = LongToHandle(GetCurrentProcessId());
  426. Info.ThreadId = LongToHandle(GetCurrentThreadId());
  427. Result = WinStationQueryInformation(
  428. SERVERNAME_CURRENT,
  429. LogonId,
  430. WinStationUserToken,
  431. &Info,
  432. sizeof(Info),
  433. &ReturnLength
  434. );
  435. if( !Result ) {
  436. DBGPRINT(("GetWinStationUserToken: Error %d getting UserToken LogonId %d\n",GetLastError(),LogonId));
  437. return( FALSE );
  438. }
  439. //
  440. // The token returned is a duplicate of a primary token.
  441. //
  442. // We must make it into an IMPERSONATION TOKEN or the
  443. // AccessCheck() routine will fail since it only operates
  444. // against impersonation tokens.
  445. //
  446. InitializeObjectAttributes( &ObjA, NULL, 0L, NULL, NULL );
  447. SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
  448. SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
  449. SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
  450. SecurityQualityOfService.EffectiveOnly = FALSE;
  451. ObjA.SecurityQualityOfService = &SecurityQualityOfService;
  452. Status = NtDuplicateToken( Info.UserToken,
  453. 0, // inherit granted accesses TOKEN_IMPERSONATE
  454. &ObjA,
  455. FALSE,
  456. TokenImpersonation,
  457. &ImpersonationToken );
  458. if ( !NT_SUCCESS( Status ) ) {
  459. DBGPRINT(("GetWinStationUserToken: Error %d duping UserToken to impersonation LogonId %d\n",GetLastError(),LogonId));
  460. NtClose( Info.UserToken );
  461. return( FALSE );
  462. }
  463. // return the impersonation token
  464. *pUserToken = ImpersonationToken;
  465. NtClose( Info.UserToken );
  466. return( TRUE );
  467. }
  468. //
  469. // This is not in winnt.h, but in ntseapi.h which we can not
  470. // include readily since we are a WIN32 program as we 'hide' this
  471. // new information type from WIN32 programs.
  472. //
  473. /*****************************************************************************
  474. *
  475. * GetClientLogonId
  476. *
  477. * Get the logonid from the client who we should be impersonating. If any
  478. * errors, we return 0 to mean the Console Logon Id since this may be a
  479. * remote network call.
  480. *
  481. * ENTRY:
  482. *
  483. * EXIT:
  484. * STATUS_SUCCESS - no error
  485. *
  486. ****************************************************************************/
  487. ULONG
  488. GetClientLogonId()
  489. {
  490. BOOL Result;
  491. HANDLE TokenHandle;
  492. ULONG LogonId, ReturnLength;
  493. //
  494. // We should be impersonating the client, so we will get the
  495. // LogonId from out token.
  496. //
  497. // We may not have a valid one if this is a remote network
  498. // connection.
  499. Result = OpenThreadToken(
  500. GetCurrentThread(),
  501. TOKEN_QUERY,
  502. FALSE, // Use impersonation
  503. &TokenHandle
  504. );
  505. if( Result ) {
  506. // This identifies which WinStation is making this request.
  507. //
  508. Result = GetTokenInformation(
  509. TokenHandle,
  510. TokenSessionId,
  511. &LogonId,
  512. sizeof(LogonId),
  513. &ReturnLength
  514. );
  515. if( Result ) {
  516. #if DBG
  517. if( ReturnLength != sizeof(LogonId) ) {
  518. DbgPrint("LOCALSPOOL: CompleteRead: ReturnLength %d != sizeof(LogonId)\n", ReturnLength );
  519. }
  520. #endif
  521. }
  522. else {
  523. DBGPRINT(("SYSLIB: Error getting token information %d\n", GetLastError()));
  524. LogonId = 0; // Default to console
  525. }
  526. CloseHandle( TokenHandle );
  527. }
  528. else {
  529. TRACE0(("SYSLIB: Error opening token %d\n", GetLastError()));
  530. LogonId = 0;
  531. }
  532. return( LogonId );
  533. }
  534. /*****************************************************************************
  535. *
  536. * WinStationEnumeratorW
  537. *
  538. * Enumerator for WinStations
  539. *
  540. * ENTRY:
  541. * StartIndex (input)
  542. * WinStation Index to start Enumeration at
  543. *
  544. * pProc (input)
  545. * Pointer to function that gets called for each WinStation
  546. * entry.
  547. *
  548. * EXAMPLE:
  549. *
  550. * BOOLEAN
  551. * EnumCallBack(
  552. * ULONG CurrentIndex, // Current Index of this entry
  553. * PLOGONIDW pInfo, // WinStation Entry
  554. * ULONG_PTR lParam // Passed through from caller of WinStationEnumeratorW
  555. * );
  556. *
  557. * If the EnumCallback function returns TRUE, the WinStationEnumeratorW()
  558. * continues the search. If it returns FALSE, the search is stopped.
  559. *
  560. * lParam (input)
  561. * Caller supplied argument passed through to caller supplied function
  562. *
  563. * EXIT:
  564. * TRUE - no error
  565. * FALSE - Error
  566. *
  567. ****************************************************************************/
  568. BOOLEAN
  569. WinStationEnumeratorW(
  570. ULONG StartIndex,
  571. WINSTATIONENUMPROC pProc,
  572. ULONG_PTR lParam
  573. )
  574. {
  575. BOOLEAN Result;
  576. ULONG Entries, i;
  577. ULONG ByteCount, ReqByteCount, Index;
  578. ULONG Error, CurrentIndex;
  579. PLOGONIDW ptr;
  580. ULONG QuerySize = 32;
  581. PLOGONIDW SmNameCache = NULL; // WinStation namelist
  582. Index = StartIndex;
  583. CurrentIndex = StartIndex;
  584. Entries = QuerySize;
  585. ByteCount = Entries * sizeof( LOGONIDW );
  586. SmNameCache = (PLOGONIDW)RtlAllocateHeap( RtlProcessHeap(), 0, ByteCount );
  587. if ( SmNameCache == NULL )
  588. return(FALSE);
  589. while( 1 ) {
  590. ReqByteCount = ByteCount;
  591. ptr = SmNameCache;
  592. Result = WinStationEnumerate_IndexedW( SERVERNAME_CURRENT, &Entries, ptr, &ByteCount, &Index );
  593. if( !Result ) {
  594. Error = GetLastError();
  595. if( Error == ERROR_NO_MORE_ITEMS ) {
  596. // Done
  597. RtlFreeHeap( RtlProcessHeap(), 0, SmNameCache );
  598. return(TRUE);
  599. }
  600. else if( Error == ERROR_ALLOTTED_SPACE_EXCEEDED ) {
  601. // Entries contains the maximum query size
  602. if( QuerySize <= Entries ) {
  603. DBGPRINT(("CPMMON: SM Query Size < RetCapable. ?View Memory Leak? Query %d, Capable %d\n", QuerySize, Entries ));
  604. QuerySize--; // See when it recovers. On retail, it will still work.
  605. }
  606. else {
  607. // We asked for more than it can handle
  608. QuerySize = Entries;
  609. }
  610. if( QuerySize == 0 ) {
  611. RtlFreeHeap( RtlProcessHeap(), 0, SmNameCache );
  612. return(FALSE);
  613. }
  614. Entries = QuerySize;
  615. ByteCount = Entries * sizeof( LOGONIDW );
  616. continue;
  617. }
  618. else {
  619. // Other error
  620. DBGPRINT(("CPMMON: Error emumerating WinStations %d\n",Error));
  621. RtlFreeHeap( RtlProcessHeap(), 0, SmNameCache );
  622. return(FALSE);
  623. }
  624. }
  625. ASSERT( ByteCount <= ReqByteCount );
  626. // We got some entries, now call the enumerator function
  627. for( i=0; i < Entries; i++ ) {
  628. Result = pProc( CurrentIndex, &SmNameCache[i], lParam );
  629. CurrentIndex++;
  630. if( !Result ) {
  631. // The Enumerator proc wants us to stop the search
  632. RtlFreeHeap( RtlProcessHeap(), 0, SmNameCache );
  633. return(TRUE);
  634. }
  635. }
  636. } // Outer while
  637. return(FALSE);
  638. }