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.

1810 lines
49 KiB

  1. // SessionResolver.cpp : Implementation of CSessionResolver
  2. #include "stdafx.h"
  3. #include "SAFSessionResolver.h"
  4. #include "SessionResolver.h"
  5. #include <sddl.h>
  6. #include <userenv.h>
  7. #include <winerror.h>
  8. #include <wtsapi32.h>
  9. #include <winsta.h>
  10. #include <wchar.h>
  11. #include <stdarg.h>
  12. #include <io.h>
  13. #include <fcntl.h>
  14. #include <stdio.h>
  15. #include <sys/types.h>
  16. #include <sys/stat.h>
  17. #define ANSI
  18. #include <stdarg.h>
  19. #include <psapi.h>
  20. #include <rderror.h>
  21. const WCHAR c_wszHelpCtr[] = L"\\PCHealth\\HelpCtr\\Binaries\\HelpCtr.exe\"";
  22. const WCHAR c_wszCmdLine[] = L" -Mode \"hcp://system/Remote Assistance/RAHelpeeAcceptLayout.xml\" -url ";
  23. const WCHAR c_wszRdsAddin[] = L"rdsaddin.exe";
  24. #define ARRAYSIZE(x) sizeof(x)/sizeof(x[0])
  25. #define BUF_SZ 200
  26. typedef struct _WTS_USER_SESSION_INFO {
  27. DWORD dwIndex; // Index into full table of these
  28. DWORD dwSessionId; // WTS Session ID
  29. HANDLE hUserToken; // Access token for user
  30. HANDLE hEvent; // filled in by launchex, yank on this to say "yes"
  31. HANDLE hProcess; // filled in by CreateProcessAsUser
  32. HANDLE hThread; // so's this
  33. DWORD dwProcessId; // and this
  34. DWORD dwThreadId; // and this
  35. } WTS_USER_SESSION_INFO, *PWTS_USER_SESSION_INFO;
  36. // stolen from internal/ds/inc
  37. #define RtlGenRandom SystemFunction036
  38. extern "C" {
  39. BOOL WINAPI
  40. RtlGenRandom(
  41. OUT PVOID RandomBuffer,
  42. IN ULONG RandomBufferLength
  43. );
  44. }
  45. /*
  46. * Forward declarations
  47. */
  48. PSID GetRealSID( BSTR pTextSID);
  49. DWORD getUserName(PSID pUserSID, WCHAR **lpName, WCHAR **lpDomain);
  50. HANDLE launchEx(PSID pUserSID, WTS_USER_SESSION_INFO *UserInfo, WCHAR *ConnectParms, WCHAR *HelpUrl, WCHAR *lpName, WCHAR *lpDomain, WCHAR *expertHelpBlob, WCHAR *userHelpBlob, SECURITY_ATTRIBUTES *sa, WCHAR *lpszMutex);
  51. DWORD GetUserSessions(PSID pUserSID, PWTS_USER_SESSION_INFO *pUserTbl, DWORD *pEntryCnt, WCHAR *lpName, WCHAR *lpDomain);
  52. PSECURITY_DESCRIPTOR CreateSd(PSID pUserSID);
  53. BOOL SecurityCheck(PSID pUserSID);
  54. DWORD localKill(WTS_USER_SESSION_INFO *SessInfo, LPTHREAD_START_ROUTINE killThrd, LPSECURITY_ATTRIBUTES lpSA);
  55. LPTHREAD_START_ROUTINE getKillProc(void);
  56. BOOL ListFind(PSPLASHLIST pSplash, PSID user);
  57. BOOL ListInsert(PSPLASHLIST pSplash, PSID user);
  58. BOOL ListDelete(PSPLASHLIST pSplash, PSID user);
  59. BOOL GetPropertyValueFromBlob(BSTR bstrHelpBlob, WCHAR * pName, WCHAR** ppValue);
  60. /************ things that should remain as defines ****************/
  61. // Some environment variables used to communicate with scripts
  62. #define ENV_USER L"USERNAME"
  63. #define ENV_DOMAIN L"USERDOMAIN"
  64. #define ENV_EVENT L"PCHEVENTNAME"
  65. #define ENV_INDEX L"PCHSESSIONENUM"
  66. #define ENV_PARMS L"PCHCONNECTPARMS"
  67. #define EVENT_PREFIX L"Alex:PCH"
  68. #define MODULE_NAME L"safrslv"
  69. // I can't imagine a user having more logins than this on one server, but...
  70. #define MAX_SESSIONS 30 // used to be 256
  71. /************ our debug spew stuff ******************/
  72. void DbgSpew(int DbgClass, BSTR lpFormat, va_list ap);
  73. void TrivialSpew(BSTR lpFormat, ...);
  74. void InterestingSpew(BSTR lpFormat, ...);
  75. void ImportantSpew(BSTR lpFormat, ...);
  76. void HeinousESpew(BSTR lpFormat, ...);
  77. void HeinousISpew(BSTR lpFormat, ...);
  78. #define DBG_MSG_TRIVIAL 0x001
  79. #define DBG_MSG_INTERESTING 0x002
  80. #define DBG_MSG_IMPORTANT 0x003
  81. #define DBG_MSG_HEINOUS 0x004
  82. #define DBG_MSG_DEST_DBG 0x010
  83. #define DBG_MSG_DEST_FILE 0x020
  84. #define DBG_MSG_DEST_EVENT 0x040
  85. #define DBG_MSG_TIME_MSGS 0x080
  86. #define DBG_MSG_CLASS_ERROR 0x100
  87. #define DBG_MSG_CLASS_SECURE 0x200
  88. #define TRIVIAL_MSG(msg) TrivialSpew msg
  89. #define INTERESTING_MSG(msg) InterestingSpew msg
  90. #define IMPORTANT_MSG(msg) ImportantSpew msg
  91. #define HEINOUS_E_MSG(msg) HeinousESpew msg
  92. #define HEINOUS_I_MSG(msg) HeinousISpew msg
  93. /* Strings for some error spewage. I waste the space since these friendly strings
  94. * do make it into the Event Logs...
  95. */
  96. WCHAR *lpszConnectState[] = {
  97. L"State_Active",
  98. L"State_Connected",
  99. L"State_ConnectQuery",
  100. L"State_Shadow",
  101. L"State_Disconnected",
  102. L"State_Idle",
  103. L"State_Listen",
  104. L"State_Reset",
  105. L"State_Down",
  106. L"State_Init"
  107. };
  108. /*
  109. * This global flag controls the amount of spew that we
  110. * produce. Legit values are as follows:
  111. * 1 = Trivial msgs displayed
  112. * 2 = Interesting msgs displayed
  113. * 3 = Important msgs displayed
  114. * 4 = only the most Heinous msgs displayed
  115. * The ctor actually sets this to 3 by default, but it can
  116. * be overridden by setting:
  117. * HKLM, Software/Microsoft/SAFSessionResolver, DebugSpew, DWORD
  118. */
  119. int gDbgFlag = 0x1;
  120. int iDbgFileHandle = 0;
  121. long lSessionTag;
  122. /////////////////////////////////////////////////////////////////////////////
  123. // CSessionResolver Methods
  124. /*************************************************************
  125. *
  126. * NewResolveTSRDPSessionID(ConnectParms, userSID, *sessionID)
  127. * Returns the WTS SessionID for the one enabled and
  128. * ready to accept Remote Control.
  129. *
  130. * RETURN CODES:
  131. * WTS_Session_ID Connection accepted by user
  132. * RC_REFUSED Connection refused by user
  133. * RC_TIMEOUT User never responded
  134. * NONE_ACTIVE Found no active WTS sessions
  135. * API_FAILURE Something bad happened
  136. *
  137. *************************************************************/
  138. STDMETHODIMP
  139. CSessionResolver::ResolveUserSessionID(
  140. /*[in]*/BSTR connectParms,
  141. /*[in]*/BSTR userSID,
  142. /*[in]*/ BSTR expertHelpBlob,
  143. /*[in]*/ BSTR userHelpBlob,
  144. /*[in]*/ ULONG_PTR hShutDown,
  145. /*[out, retval]*/long *sessionID,
  146. /*[in*/ DWORD dwPID,
  147. /*[out]*/ULONG_PTR *hHelpCtr
  148. ,/*[out, retval]*/int *userResponse
  149. )
  150. {
  151. INTERESTING_MSG((L"CSessionResolver::ResolveUserSessionID"));
  152. DWORD result;
  153. HANDLE hRdsAddin = NULL;
  154. HANDLE hReserveMutex = NULL;
  155. PSID pRealSID = NULL;
  156. WCHAR *pUsername=NULL, *pDomainname=NULL;
  157. PWTS_USER_SESSION_INFO pUserSessionInfo=NULL;
  158. PSECURITY_DESCRIPTOR pSD=NULL;
  159. HRESULT ret_code;
  160. int i;
  161. int TsIndex=-1;
  162. DWORD dwUserSessionCnt, dwSessionCnt;
  163. HANDLE pHandles[(MAX_SESSIONS*2)+2];
  164. DWORD dwhIndex = 0;
  165. SECURITY_ATTRIBUTES sa;
  166. BOOL bAlreadyHelped, bRemoval=FALSE;
  167. WCHAR *pExpertId=NULL, *pUserId=NULL;
  168. /* param validation */
  169. if (!connectParms || !userSID || !sessionID || !hHelpCtr
  170. || !userResponse
  171. )
  172. {
  173. IMPORTANT_MSG((L"Invalid params ConnectParms=0x%x, UserSID=0x%x, SessionID=0x%x", connectParms, userSID, sessionID));
  174. ret_code = E_INVALIDARG;
  175. goto done;
  176. }
  177. if( m_bCriticalError )
  178. {
  179. IMPORTANT_MSG( (L"Possible low resources-InitCritSec failed" ) );
  180. ret_code = E_FAIL;
  181. goto done;
  182. }
  183. // set a default ret code
  184. *userResponse = SAFERROR_INTERNALERROR;
  185. if (dwPID)
  186. hRdsAddin = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);
  187. if (hRdsAddin)
  188. {
  189. /*
  190. * Make certain this is the correct process. If it is not
  191. * (rdsaddin.exe) then we bail out.
  192. */
  193. DWORD dwRes;
  194. WCHAR szTmp[32];
  195. dwRes = GetModuleBaseNameW(hRdsAddin, NULL, szTmp, ARRAYSIZE(szTmp));
  196. if (!dwRes || StrCmpI(c_wszRdsAddin, szTmp))
  197. {
  198. IMPORTANT_MSG((L"ERROR: the process handle for rdsaddin.exe has been recycled"));
  199. CloseHandle(hRdsAddin);
  200. hRdsAddin = 0;
  201. }
  202. }
  203. if (!hRdsAddin)
  204. {
  205. // If we don't have a process handle, it is because
  206. // the expert has already cancelled
  207. ret_code = E_ACCESSDENIED;
  208. *userResponse = SAFERROR_CANTFORMLINKTOUSERSESSION;
  209. goto done;
  210. }
  211. pRealSID = GetRealSID(userSID);
  212. if (!pRealSID)
  213. {
  214. IMPORTANT_MSG((L"GetRealSID failed"));
  215. ret_code = E_ACCESSDENIED;
  216. *userResponse = SAFERROR_INVALIDPARAMETERSTRING;
  217. goto done;
  218. }
  219. EnterCriticalSection(&m_CritSec);
  220. bAlreadyHelped = ListFind(m_pSplash, pRealSID);
  221. ListInsert(m_pSplash, pRealSID);
  222. // mark the SID for removal
  223. bRemoval=TRUE;
  224. LeaveCriticalSection(&m_CritSec);
  225. if (bAlreadyHelped)
  226. {
  227. INTERESTING_MSG((L"Helpee already has a ticket on the screen"));
  228. *sessionID = 0;
  229. *userResponse = SAFERROR_HELPEECONSIDERINGHELP;
  230. ret_code = E_ACCESSDENIED;
  231. goto done;
  232. }
  233. TRIVIAL_MSG((L"userHelpBlob=[%s], expertHelpBlob=[%s]",userHelpBlob?userHelpBlob:L"NULL", expertHelpBlob?expertHelpBlob:L"NULL" ));
  234. /* check password: skip is UNSOLICITED=1 */
  235. if (!GetPropertyValueFromBlob(userHelpBlob, L"UNSOLICITED", &pUserId) ||
  236. !pUserId || *pUserId != L'1')
  237. {
  238. // Need to check password.
  239. if (pUserId)
  240. {
  241. LocalFree(pUserId);
  242. pUserId = NULL;
  243. }
  244. if (GetPropertyValueFromBlob(userHelpBlob, L"PASS", &pUserId))
  245. {
  246. if (!GetPropertyValueFromBlob(expertHelpBlob, L"PASS", &pExpertId) || wcscmp(pExpertId, pUserId) != 0)
  247. {
  248. IMPORTANT_MSG((L"Passwords don't match, x:%s, n:%s ", pExpertId, pUserId));
  249. ret_code = E_ACCESSDENIED;
  250. *userResponse = SAFERROR_INVALIDPASSWORD;
  251. goto done;
  252. }
  253. }
  254. }
  255. /* get the user's account strings */
  256. if (!getUserName(pRealSID, &pUsername, &pDomainname))
  257. {
  258. DWORD error = GetLastError();
  259. HEINOUS_E_MSG((L"getUserName() failed, err=0x%x", error));
  260. ret_code = E_ACCESSDENIED;
  261. *userResponse = SAFERROR_INVALIDPARAMETERSTRING;
  262. goto done;
  263. }
  264. /*
  265. * Get a list of all the active sessions on this WTS Server
  266. * For a specific user
  267. */
  268. // keeps the compiler happy
  269. dwSessionCnt = 0;
  270. result = GetUserSessions(pRealSID,
  271. &pUserSessionInfo,
  272. &dwUserSessionCnt,
  273. pUsername, pDomainname);
  274. if (!result )
  275. {
  276. IMPORTANT_MSG((L"GetUserSessions failed %08x", GetLastError()));
  277. ret_code = E_FAIL;
  278. goto done;
  279. }
  280. /* If no sessions are found, then exit! */
  281. if (dwUserSessionCnt == 0)
  282. {
  283. INTERESTING_MSG((L"no sessions found"));
  284. *sessionID = 0;
  285. *userResponse = SAFERROR_HELPEENOTFOUND;
  286. ret_code = E_ACCESSDENIED;
  287. goto done;
  288. }
  289. /* make certain we don't overflow our handle buffers */
  290. else if (dwUserSessionCnt > MAX_SESSIONS)
  291. {
  292. HEINOUS_I_MSG((L"Found %d active sessions for %ws/%ws, limitting to %d", dwUserSessionCnt, pDomainname, pUsername, MAX_SESSIONS));
  293. i = MAX_SESSIONS;
  294. // free the extra WTS tokens
  295. while (i < (int)dwSessionCnt)
  296. {
  297. if (pUserSessionInfo[i].hUserToken)
  298. CloseHandle(pUserSessionInfo[i].hUserToken);
  299. i++;
  300. }
  301. dwUserSessionCnt = MAX_SESSIONS;
  302. }
  303. pSD = CreateSd(pRealSID);
  304. if (!pSD)
  305. {
  306. IMPORTANT_MSG((L"CreateSd failed err=%08x", GetLastError()));
  307. ret_code = E_ACCESSDENIED;
  308. goto done;
  309. }
  310. sa.nLength = sizeof(sa);
  311. sa.lpSecurityDescriptor = pSD;
  312. sa.bInheritHandle = FALSE;
  313. memset(&pHandles[0], 0, sizeof(pHandles));
  314. pHandles[0] = hRdsAddin;
  315. lSessionTag = InterlockedIncrement(&m_lSessionTag);
  316. WCHAR lpNameBfr[256];
  317. wnsprintfW(lpNameBfr, ARRAYSIZE(lpNameBfr), L"Global\\AlexDont%lx", lSessionTag);
  318. hReserveMutex = CreateMutex(&sa, FALSE, &lpNameBfr[0]);
  319. if (!hReserveMutex || ERROR_ALREADY_EXISTS == GetLastError())
  320. {
  321. /*
  322. * If we failed to create this event, it is most likely because one is already
  323. * in name-space, perhaps an event, or else a mutex... In any case, we will
  324. * try again, with a more random name. If that fails, then we must bail out.
  325. */
  326. long lRand;
  327. if (hReserveMutex)
  328. CloseHandle(hReserveMutex);
  329. RtlGenRandom(&lRand, sizeof(lRand));
  330. wnsprintfW(lpNameBfr, ARRAYSIZE(lpNameBfr), L"Global\\%lx%lx",
  331. lRand, lSessionTag);
  332. hReserveMutex = CreateMutex(&sa, FALSE, &lpNameBfr[0]);
  333. if (!hReserveMutex || ERROR_ALREADY_EXISTS == GetLastError())
  334. {
  335. if (hReserveMutex)
  336. CloseHandle(hReserveMutex);
  337. hReserveMutex = 0;
  338. HEINOUS_E_MSG((L"The named event \"%s\" was in use- potential security issue, so Remote Assistance will be denied.", lpNameBfr));
  339. goto done;
  340. }
  341. }
  342. /*
  343. * Start up the HelpCtr in all the various TS sessions
  344. */
  345. ret_code = S_OK;
  346. for(i=0; i<(int)dwUserSessionCnt; i++)
  347. {
  348. TRIVIAL_MSG((L"calling launchEx[%d] for session %d", i, pUserSessionInfo[i].dwSessionId));
  349. pUserSessionInfo[i].hProcess = launchEx(pRealSID,
  350. &pUserSessionInfo[i],
  351. connectParms, m_bstrResolveURL,
  352. pUsername, pDomainname,
  353. expertHelpBlob,userHelpBlob, &sa, lpNameBfr);
  354. if( pUserSessionInfo[i].hProcess == NULL || pUserSessionInfo[i].hEvent == NULL )
  355. {
  356. ret_code = E_FAIL;
  357. break;
  358. }
  359. pHandles[i+1] = pUserSessionInfo[i].hProcess;
  360. pHandles[i+1+dwUserSessionCnt] = pUserSessionInfo[i].hEvent;
  361. }
  362. //
  363. // Last in list is our shutdown event
  364. //
  365. pHandles[dwUserSessionCnt*2+1] = (HANDLE)hShutDown;
  366. if( ret_code == S_OK )
  367. {
  368. /*
  369. * Then wait for somebody to click on a "Yes", or a "No"
  370. */
  371. // We use CoWaitForMultipleHandles otherwise, rdsaddin and sessmg
  372. // deadlock since sessmgr might be apartment threaded
  373. TRIVIAL_MSG((L"Waiting. m_iWaitDuration: %ld seconds", m_iWaitDuration/1000));
  374. ret_code = CoWaitForMultipleHandles (
  375. COWAIT_ALERTABLE,
  376. m_iWaitDuration,
  377. (dwUserSessionCnt*2)+2,
  378. pHandles,
  379. &dwhIndex
  380. );
  381. // try to take the mutex (in case somebody asks for it later...
  382. CoWaitForMultipleHandles (COWAIT_ALERTABLE, 0, 1, &hReserveMutex, &result);
  383. }
  384. if( ret_code == S_OK )
  385. {
  386. /*
  387. * Then wait for somebody to click on a "Yes", or a "No"
  388. */
  389. // We use CoWaitForMultipleHandles otherwise, rdsaddin and sessmg
  390. // deadlock since sessmgr might be apartment threaded
  391. TRIVIAL_MSG((L"Waiting. m_iWaitDuration: %ld seconds", m_iWaitDuration/1000));
  392. ret_code = CoWaitForMultipleHandles (
  393. COWAIT_ALERTABLE,
  394. m_iWaitDuration,
  395. (dwUserSessionCnt*2)+1,
  396. pHandles,
  397. &dwhIndex
  398. );
  399. // try to take the mutex (in case somebody asks for it later...
  400. CoWaitForMultipleHandles (COWAIT_ALERTABLE, 0, 1, &hReserveMutex, &result);
  401. }
  402. if (S_OK == ret_code)
  403. {
  404. if(dwhIndex == dwUserSessionCnt*2+1)
  405. {
  406. // shutdown event has signaled.
  407. TsIndex = -1;
  408. ret_code = E_ACCESSDENIED;
  409. *userResponse = SAFERROR_CANTFORMLINKTOUSERSESSION;
  410. }
  411. else if (dwhIndex > dwUserSessionCnt)
  412. {
  413. /* somebody said "yes" */
  414. long l_iWaitDuration = m_iWaitDuration/2;
  415. TsIndex = dwhIndex-dwUserSessionCnt-1;
  416. TRIVIAL_MSG((L"User responded YES for session 0x%x, waiting for Salem setup", TsIndex));
  417. /*
  418. * Then we wait for the HelpCtr scripts to establish the Salem connection
  419. */
  420. ResetEvent(pHandles[dwhIndex]);
  421. ret_code = CoWaitForMultipleHandles (COWAIT_ALERTABLE, l_iWaitDuration, 1, &pHandles[dwhIndex], &result);
  422. if (ret_code != S_OK || result != WAIT_OBJECT_0)
  423. {
  424. IMPORTANT_MSG((L"Bad script- it didn't signal acknowledgement within %d seconds", l_iWaitDuration/1000));
  425. bRemoval = TRUE;
  426. ret_code = E_ACCESSDENIED;
  427. *userResponse = SAFERROR_INTERNALERROR;
  428. TsIndex = -1;
  429. }
  430. else
  431. {
  432. TRIVIAL_MSG((L"HelpCtr set up Salem in time."));
  433. *userResponse = SAFERROR_NOERROR;
  434. // mark the SID for non-removal
  435. bRemoval=FALSE;
  436. *hHelpCtr = NULL; // start with NULL
  437. // return the hProc of the HelpCenter instance that wants to start RA
  438. DuplicateHandle(GetCurrentProcess(), pUserSessionInfo[TsIndex].hProcess,
  439. hRdsAddin, (HANDLE *)hHelpCtr, SYNCHRONIZE, FALSE, 0);
  440. }
  441. }
  442. else if (dwhIndex == 0)
  443. {
  444. // we got here because the expert bailed out, or we lost the connection
  445. INTERESTING_MSG((L"Expert killed RdsAddin"));
  446. TsIndex = -1;
  447. ret_code = E_ACCESSDENIED;
  448. *userResponse = SAFERROR_CANTFORMLINKTOUSERSESSION;
  449. }
  450. else
  451. {
  452. /*
  453. * We get here because the novice "killed" a HelpCtr session
  454. * or else the novice just said "NO"
  455. */
  456. INTERESTING_MSG((L"User killed session or clicked NO for session 0x%x", dwhIndex-1));
  457. /* this keeps us from trying to kill something the user has already closed */
  458. TsIndex = dwhIndex-1;
  459. ret_code = E_ACCESSDENIED;
  460. *userResponse = SAFERROR_HELPEESAIDNO;
  461. }
  462. }
  463. else if (RPC_S_CALLPENDING == ret_code)
  464. {
  465. TRIVIAL_MSG((L"User response timed out after %d seconds", m_iWaitDuration/1000));
  466. TsIndex = -1;
  467. ret_code = E_PENDING;
  468. *userResponse = SAFERROR_HELPEENEVERRESPONDED;
  469. }
  470. else
  471. {
  472. IMPORTANT_MSG((L"WaitForObject failed %08x err=%08x", result, GetLastError()));
  473. TsIndex = -1;
  474. ret_code = E_FAIL;
  475. }
  476. /*
  477. * Then close all the windows (except the one in TsIndex)
  478. */
  479. for(i=0; i<(int)dwUserSessionCnt; i++)
  480. {
  481. LPTHREAD_START_ROUTINE lpKill = getKillProc();
  482. if (pUserSessionInfo[i].dwIndex != TsIndex &&
  483. pUserSessionInfo[i].hProcess)
  484. {
  485. /* This has to be done for each instance, since we call into the process
  486. * to kill itself. If we did not get "lpKill" for each seperate occurance
  487. * of HelpCtr, then Very Bad Things could happen...
  488. */
  489. TRIVIAL_MSG((L"Killing HelpCtr in process %d", pUserSessionInfo[i].hProcess));
  490. localKill(&pUserSessionInfo[i], lpKill, &sa);
  491. }
  492. }
  493. if (ret_code == S_OK)
  494. *sessionID = (long) pUserSessionInfo[TsIndex].dwSessionId;
  495. done:
  496. if (bRemoval)
  497. {
  498. // remove the SID from the list
  499. EnterCriticalSection(&m_CritSec);
  500. ListDelete(m_pSplash, pRealSID);
  501. LeaveCriticalSection(&m_CritSec);
  502. }
  503. if (hReserveMutex)
  504. CloseHandle(hReserveMutex);
  505. if (hRdsAddin)
  506. CloseHandle(hRdsAddin);
  507. if (pRealSID)
  508. LocalFree(pRealSID);
  509. if (pUserSessionInfo)
  510. {
  511. /* close all the handles */
  512. for(i=0; i<(int)dwUserSessionCnt; i++)
  513. {
  514. if (pUserSessionInfo[i].hProcess)
  515. CloseHandle(pUserSessionInfo[i].hProcess);
  516. if (pUserSessionInfo[i].hUserToken)
  517. CloseHandle(pUserSessionInfo[i].hUserToken);
  518. if (pUserSessionInfo[i].hEvent)
  519. CloseHandle(pUserSessionInfo[i].hEvent);
  520. }
  521. LocalFree(pUserSessionInfo);
  522. }
  523. if (pUsername)
  524. LocalFree(pUsername);
  525. if (pDomainname)
  526. LocalFree(pDomainname);
  527. if (pSD)
  528. LocalFree(pSD);
  529. if (pUserId) LocalFree(pUserId);
  530. if (pExpertId) LocalFree(pExpertId);
  531. INTERESTING_MSG((L"CSessionResolver::ResolveUserSessionID returns %x\n", ret_code ));
  532. return ret_code;
  533. }
  534. /*************************************************************
  535. *
  536. * OnDisconnect([in] BSTR connectParms, [in] BSTR userSID, [in] long sessionID)
  537. * Notifies us when an RA session ends
  538. *
  539. * NOTES:
  540. * This is called so we can maintain the state of our user prompts
  541. *
  542. * WARNING: ACHTUNG: ATTENZIONE:
  543. * This method must do a minimal amount of work before returning
  544. * and must NEVER do anything that would cause COM to pump
  545. * messages. To do so would screw Salem immensely.a
  546. *
  547. * RETURN CODES:
  548. * NONE_ACTIVE Found no active WTS sessions
  549. * API_FAILURE Something bad happened
  550. *
  551. *************************************************************/
  552. STDMETHODIMP
  553. CSessionResolver::OnDisconnect(
  554. /*[in]*/BSTR connectParms,
  555. /*[in]*/BSTR userSID,
  556. /*[in]*/long sessionID
  557. )
  558. {
  559. PSID pRealSID;
  560. if (!connectParms || !userSID)
  561. {
  562. HEINOUS_I_MSG((L"Invalid params in OnDisconnect- ConnectParms=0x%x, UserSID=0x%x", connectParms, userSID));
  563. return E_INVALIDARG;
  564. }
  565. INTERESTING_MSG((L"CSessionResolver::OnDisconnect-(%ws)", userSID));
  566. pRealSID = GetRealSID(userSID);
  567. if (pRealSID)
  568. {
  569. EnterCriticalSection(&m_CritSec);
  570. ListDelete(m_pSplash, pRealSID);
  571. LeaveCriticalSection(&m_CritSec);
  572. LocalFree(pRealSID);
  573. }
  574. INTERESTING_MSG((L"CSessionResolver::OnDisconnect; leaving"));
  575. return S_OK;
  576. }
  577. /*************************************************************
  578. *
  579. * GetRealSID([in] BSTR pTextSID)
  580. * Converts a string-based SID into a REAL usable SID
  581. *
  582. * NOTES:
  583. * This is a stub into "ConvertStringSidToSid".
  584. *
  585. * RETURN CODES:
  586. * NULL Failed for some reason
  587. * PSID Pointer to a real SID. Must be
  588. * freed with "LocalFree"
  589. *
  590. *************************************************************/
  591. PSID GetRealSID( BSTR pTextSID)
  592. {
  593. PSID pRetSID = NULL;
  594. if (!ConvertStringSidToSidW(pTextSID, &pRetSID))
  595. IMPORTANT_MSG((L"ConvertStringSidToSidW(%ws) failed %08x\n", pTextSID, GetLastError()));
  596. return pRetSID;
  597. }
  598. /*************************************************************
  599. *
  600. * launchEx(PSID, WTS_USER_SESSION_INFO, char * ConnectParms, char * EventName)
  601. *
  602. *
  603. * RETURN CODES:
  604. * 0 Failed to start process
  605. * <> HANDLE to started process
  606. *
  607. *************************************************************/
  608. HANDLE launchEx(PSID pUserSID, WTS_USER_SESSION_INFO *UserInfo,
  609. WCHAR *ConnectParms, WCHAR *HelpPageURL,
  610. WCHAR *pUsername, WCHAR *pDomainname,
  611. WCHAR *expertHelpBlob, WCHAR *userHelpBlob,
  612. SECURITY_ATTRIBUTES *sa,
  613. WCHAR *lpszMutex
  614. )
  615. {
  616. BOOL result=FALSE;
  617. HANDLE retval = 0;
  618. STARTUPINFOW StartUp;
  619. PROCESS_INFORMATION p_i;
  620. WCHAR buf1[BUF_SZ], buf2[BUF_SZ], *lpUtf8ConnectParms=NULL;
  621. static WCHAR *szEnvUser = ENV_USER;
  622. static WCHAR *szEnvDomain = ENV_DOMAIN;
  623. static WCHAR *szEnvEvent = ENV_EVENT;
  624. static WCHAR *szEnvIndex = ENV_INDEX;
  625. static WCHAR *szEnvParms = ENV_PARMS;
  626. static WCHAR *szEnvExpertBlob = L"PCHEXPERTBLOB";
  627. static WCHAR *szEnvUserBlob = L"PCHUSERBLOB";
  628. static WCHAR *szEnvMutex = L"PCHMutexName";
  629. VOID *pEnvBlock = NULL;
  630. DWORD dwUsername=0, dwDomainname=0, dwStrSz;
  631. WCHAR *wszExe = NULL;
  632. dwStrSz = 3 + GetSystemWindowsDirectory(NULL, 0);
  633. dwStrSz += wcslen(c_wszHelpCtr);
  634. dwStrSz += wcslen(c_wszCmdLine);
  635. dwStrSz += wcslen(HelpPageURL);
  636. __try { wszExe = (LPWSTR)_alloca(dwStrSz * sizeof(*wszExe)); }
  637. __except(EXCEPTION_STACK_OVERFLOW) { wszExe = NULL; }
  638. if (wszExe == NULL)
  639. {
  640. SetLastError(ERROR_OUTOFMEMORY);
  641. goto done;
  642. }
  643. ZeroMemory(wszExe, dwStrSz * sizeof(*wszExe));
  644. wszExe[0] = L'\"';
  645. GetSystemWindowsDirectory(wszExe+1, dwStrSz-1);
  646. wcscat(wszExe, c_wszHelpCtr);
  647. wcscat(wszExe, c_wszCmdLine);
  648. wcscat(wszExe, HelpPageURL);
  649. /*
  650. * Here, we must start up a Help Center script in a WTS Session
  651. * It gets a bit sticky, though as we do not have access to the
  652. * user's desktop (any desktop), and this must appear on only
  653. * one particular desktop. I am relying on the WTS-User-Token
  654. * to get the desktop for me.
  655. *
  656. * The main component is our call to CreateProcessAsUser.
  657. * Before we call that we must:
  658. * Set up Environment as follows:
  659. * PATH=%SystemPath%
  660. * WINDIR=SystemRoot%
  661. * USERNAME=(from WTS)
  662. * USERDOMAIN=
  663. * PCHEVENTNAME=EventName
  664. * PCHSESSIONENUM=UserInfo->dwIndex
  665. * PCHCONNECTPARMS=ConnectParms
  666. */
  667. TRIVIAL_MSG((L"Launch %ws", wszExe));
  668. /* Step on the ENVIRONMENT */
  669. WCHAR lpNameBfr[256];
  670. wnsprintfW(lpNameBfr, ARRAYSIZE(lpNameBfr), L"Global\\%ws%lx_%02d", EVENT_PREFIX, lSessionTag, UserInfo->dwIndex);
  671. UserInfo->hEvent = CreateEvent(sa, TRUE, FALSE, lpNameBfr);
  672. if (!UserInfo->hEvent || ERROR_ALREADY_EXISTS == GetLastError())
  673. {
  674. /*
  675. * If we failed to create this event, it is most likely because one is already
  676. * in name-space, perhaps an event, or else a mutex... In any case, we will
  677. * try again, with a more random name. If that fails, then we must bail out.
  678. */
  679. long lRand;
  680. if (UserInfo->hEvent)
  681. CloseHandle(UserInfo->hEvent);
  682. RtlGenRandom(&lRand, sizeof(lRand));
  683. wnsprintfW(lpNameBfr, ARRAYSIZE(lpNameBfr), L"Global\\%lx%lx%lx_%02d",
  684. lRand, lSessionTag, UserInfo->dwIndex);
  685. UserInfo->hEvent = CreateEvent(sa, TRUE, FALSE, lpNameBfr);
  686. if (!UserInfo->hEvent || ERROR_ALREADY_EXISTS == GetLastError())
  687. {
  688. if (UserInfo->hEvent)
  689. CloseHandle(UserInfo->hEvent);
  690. UserInfo->hEvent = 0;
  691. HEINOUS_E_MSG((L"The named event \"%s\" was in use- potential security issue, so Remote Assistance will be denied.", lpNameBfr));
  692. goto done;
  693. }
  694. }
  695. SetEnvironmentVariable(szEnvEvent, lpNameBfr);
  696. wsprintf(buf1, L"%d", UserInfo->dwIndex);
  697. SetEnvironmentVariable(szEnvIndex, buf1);
  698. SetEnvironmentVariable(szEnvParms, ConnectParms);
  699. SetEnvironmentVariable(szEnvMutex, lpszMutex);
  700. SetEnvironmentVariable(szEnvUser, pUsername );
  701. SetEnvironmentVariable(szEnvDomain, pDomainname );
  702. SetEnvironmentVariable(szEnvExpertBlob, expertHelpBlob);
  703. SetEnvironmentVariable(szEnvUserBlob, userHelpBlob);
  704. if (!CreateEnvironmentBlock(&pEnvBlock, UserInfo->hUserToken, TRUE))
  705. {
  706. IMPORTANT_MSG((L"CreateEnvironmentBlock failed in resolver:launchex, err=0x%x", GetLastError()));
  707. goto done;
  708. }
  709. // initialize our structs
  710. ZeroMemory(&p_i, sizeof(p_i));
  711. ZeroMemory(&StartUp, sizeof(StartUp));
  712. StartUp.cb = sizeof(StartUp);
  713. StartUp.dwFlags = STARTF_USESHOWWINDOW;
  714. StartUp.wShowWindow = SW_SHOWNORMAL;
  715. result = CreateProcessAsUserW(UserInfo->hUserToken,
  716. NULL, wszExe,
  717. NULL, NULL, FALSE,
  718. NORMAL_PRIORITY_CLASS + CREATE_UNICODE_ENVIRONMENT ,
  719. pEnvBlock, // Environment block (must use the CREATE_UNICODE_ENVIRONMENT flag)
  720. NULL, &StartUp, &p_i);
  721. if (result)
  722. {
  723. // keep a leak from happening, as we never need the hThread...
  724. CloseHandle(p_i.hThread);
  725. UserInfo->hProcess = p_i.hProcess;
  726. UserInfo->hThread = 0;
  727. UserInfo->dwProcessId = p_i.dwProcessId;
  728. UserInfo->dwThreadId = p_i.dwThreadId;
  729. retval = p_i.hProcess;
  730. TRIVIAL_MSG((L"CreateProcessAsUserW started up [%ws]", wszExe));
  731. }
  732. else
  733. {
  734. IMPORTANT_MSG((L"CreateProcessAsUserW failed, err=0x%x command line=[%ws]", GetLastError(), wszExe));
  735. result=0;
  736. }
  737. done:
  738. if (!result)
  739. {
  740. UserInfo->hProcess = 0;
  741. UserInfo->hThread = 0;
  742. UserInfo->dwProcessId = 0;
  743. UserInfo->dwThreadId = 0;
  744. if( UserInfo->hEvent != NULL )
  745. {
  746. CloseHandle( UserInfo->hEvent );
  747. UserInfo->hEvent = 0;
  748. }
  749. }
  750. // restore any memory we borrowed
  751. if (pEnvBlock) DestroyEnvironmentBlock(pEnvBlock);
  752. return retval;
  753. }
  754. /*************************************************************
  755. *
  756. * CreateSids([in] BSTR pTextSID)
  757. * Create 3 Security IDs
  758. *
  759. * NOTES:
  760. * Caller must free memory allocated to SIDs on success.
  761. *
  762. * RETURN CODES: TRUE if successfull, FALSE if not.
  763. *
  764. *************************************************************/
  765. BOOL
  766. CreateSids(
  767. PSID *BuiltInAdministrators,
  768. PSID *PowerUsers,
  769. PSID *AuthenticatedUsers
  770. )
  771. {
  772. //
  773. // An SID is built from an Identifier Authority and a set of Relative IDs
  774. // (RIDs). The Authority of interest to us SECURITY_NT_AUTHORITY.
  775. //
  776. SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
  777. //
  778. // Each RID represents a sub-unit of the authority. Two of the SIDs we
  779. // want to build, Local Administrators, and Power Users, are in the "built
  780. // in" domain. The other SID, for Authenticated users, is based directly
  781. // off of the authority.
  782. //
  783. // For examples of other useful SIDs consult the list in
  784. // \nt\public\sdk\inc\ntseapi.h.
  785. //
  786. if (!AllocateAndInitializeSid(&NtAuthority,
  787. 2, // 2 sub-authorities
  788. SECURITY_BUILTIN_DOMAIN_RID,
  789. DOMAIN_ALIAS_RID_ADMINS,
  790. 0,0,0,0,0,0,
  791. BuiltInAdministrators))
  792. {
  793. // error
  794. HEINOUS_E_MSG((L"Could not allocate security credentials for admins."));
  795. }
  796. else if (!AllocateAndInitializeSid(&NtAuthority,
  797. 2, // 2 sub-authorities
  798. SECURITY_BUILTIN_DOMAIN_RID,
  799. DOMAIN_ALIAS_RID_POWER_USERS,
  800. 0,0,0,0,0,0,
  801. PowerUsers))
  802. {
  803. // error
  804. HEINOUS_E_MSG((L"Could not allocate security credentials for power users."));
  805. FreeSid(*BuiltInAdministrators);
  806. *BuiltInAdministrators = NULL;
  807. }
  808. else if (!AllocateAndInitializeSid(&NtAuthority,
  809. 1, // 1 sub-authority
  810. SECURITY_AUTHENTICATED_USER_RID,
  811. 0,0,0,0,0,0,0,
  812. AuthenticatedUsers))
  813. {
  814. // error
  815. HEINOUS_E_MSG((L"Could not allocate security credentials for users."));
  816. FreeSid(*BuiltInAdministrators);
  817. *BuiltInAdministrators = NULL;
  818. FreeSid(*PowerUsers);
  819. *PowerUsers = NULL;
  820. } else {
  821. return TRUE;
  822. }
  823. return FALSE;
  824. }
  825. /*************************************************************
  826. *
  827. * CreateSd(void)
  828. * Creates a SECURITY_DESCRIPTOR with specific DACLs.
  829. *
  830. * NOTES:
  831. * Caller must free the returned buffer if not NULL.
  832. *
  833. * RETURN CODES:
  834. * NULL Failed for some reason
  835. * PSECURITY_DESCRIPTOR Pointer to a SECURITY_DESCRIPTOR.
  836. * Must be freed with "LocalFree"
  837. *
  838. *************************************************************/
  839. PSECURITY_DESCRIPTOR CreateSd(PSID pUserSID)
  840. {
  841. PSID AuthenticatedUsers;
  842. PSID BuiltInAdministrators;
  843. PSID PowerUsers;
  844. PSECURITY_DESCRIPTOR Sd = NULL;
  845. ULONG AclSize;
  846. ACL *Acl;
  847. if (!CreateSids(&BuiltInAdministrators,
  848. &PowerUsers,
  849. &AuthenticatedUsers))
  850. {
  851. // error
  852. IMPORTANT_MSG((L"CreateSids failed"));
  853. return NULL;
  854. }
  855. //
  856. // Calculate the size of and allocate a buffer for the DACL, we need
  857. // this value independently of the total alloc size for ACL init.
  858. //
  859. //
  860. // "- sizeof (ULONG)" represents the SidStart field of the
  861. // ACCESS_ALLOWED_ACE. Since we're adding the entire length of the
  862. // SID, this field is counted twice.
  863. //
  864. AclSize = sizeof (ACL) +
  865. (4 * (sizeof (ACCESS_ALLOWED_ACE) - sizeof (ULONG))) +
  866. GetLengthSid(AuthenticatedUsers) +
  867. GetLengthSid(BuiltInAdministrators) +
  868. GetLengthSid(PowerUsers) +
  869. GetLengthSid(pUserSID);
  870. Sd = LocalAlloc(LMEM_FIXED + LMEM_ZEROINIT,
  871. SECURITY_DESCRIPTOR_MIN_LENGTH + AclSize);
  872. if (!Sd)
  873. {
  874. IMPORTANT_MSG((L"Cound not allocate 0x%x bytes for Security Descriptor", SECURITY_DESCRIPTOR_MIN_LENGTH + AclSize));
  875. goto error;
  876. }
  877. Acl = (ACL *)((BYTE *)Sd + SECURITY_DESCRIPTOR_MIN_LENGTH);
  878. if (!InitializeAcl(Acl,
  879. AclSize,
  880. ACL_REVISION))
  881. {
  882. // error
  883. IMPORTANT_MSG((L"Cound not initialize ACL err=0x%x", GetLastError()));
  884. goto error;
  885. }
  886. TRIVIAL_MSG((L"initialized Successfully"));
  887. if (!AddAccessAllowedAce(Acl,
  888. ACL_REVISION,
  889. STANDARD_RIGHTS_ALL | GENERIC_WRITE,
  890. pUserSID))
  891. {
  892. // Failed to build the ACE granted to OWNER
  893. // (STANDARD_RIGHTS_ALL) access.
  894. IMPORTANT_MSG((L"Cound not add owner rights to ACL err=0x%x", GetLastError()));
  895. goto error;
  896. }
  897. if (!AddAccessAllowedAce(Acl,
  898. ACL_REVISION,
  899. GENERIC_READ,
  900. AuthenticatedUsers))
  901. {
  902. // Failed to build the ACE granting "Authenticated users"
  903. // (SYNCHRONIZE | GENERIC_READ) access.
  904. IMPORTANT_MSG((L"Cound not add user rights to ACL err=0x%x", GetLastError()));
  905. goto error;
  906. }
  907. if (!AddAccessAllowedAce(Acl,
  908. ACL_REVISION,
  909. GENERIC_READ | GENERIC_WRITE,
  910. PowerUsers))
  911. {
  912. // Failed to build the ACE granting "Power users"
  913. // (SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE) access.
  914. IMPORTANT_MSG((L"Cound not add power user rights to ACL err=0x%x", GetLastError()));
  915. goto error;
  916. }
  917. if (!AddAccessAllowedAce(Acl,
  918. ACL_REVISION,
  919. STANDARD_RIGHTS_ALL,
  920. BuiltInAdministrators))
  921. {
  922. // Failed to build the ACE granting "Built-in Administrators"
  923. // STANDARD_RIGHTS_ALL access.
  924. IMPORTANT_MSG((L"Cound not add admin rights to ACL err=0x%x", GetLastError()));
  925. goto error;
  926. }
  927. if (!InitializeSecurityDescriptor(Sd,SECURITY_DESCRIPTOR_REVISION))
  928. {
  929. // error
  930. IMPORTANT_MSG((L"Cound not initialize SD err=0x%x", GetLastError()));
  931. goto error;
  932. }
  933. if (!SetSecurityDescriptorDacl(Sd,
  934. TRUE,
  935. Acl,
  936. FALSE))
  937. {
  938. // error
  939. IMPORTANT_MSG((L"SetSecurityDescriptorDacl failed err=0x%x", GetLastError()));
  940. goto error;
  941. }
  942. FreeSid(AuthenticatedUsers);
  943. FreeSid(BuiltInAdministrators);
  944. FreeSid(PowerUsers);
  945. // TRIVIAL_MSG((L"CreateSd succeeded."));
  946. return Sd;
  947. error:
  948. /* A jump of last resort */
  949. if (Sd)
  950. LocalFree(Sd);
  951. // error
  952. if (AuthenticatedUsers)
  953. FreeSid(AuthenticatedUsers);
  954. if (BuiltInAdministrators)
  955. FreeSid(BuiltInAdministrators);
  956. if (PowerUsers)
  957. FreeSid(PowerUsers);
  958. return NULL;
  959. }
  960. /*************************************************************
  961. *
  962. * GetUserSessions(PSID, PWTS_USER_SESSION_INFOW , *DWORD)
  963. * Returns a table of all the active WTS Sessions for this
  964. * user, on this server.
  965. *
  966. * RETURN CODES:
  967. *
  968. * NOTES:
  969. * Should log NT Events for failures
  970. *
  971. *************************************************************/
  972. DWORD GetUserSessions(PSID pUserSID, PWTS_USER_SESSION_INFO *pUserTbl, DWORD *pEntryCnt, WCHAR *pUsername, WCHAR *pDomainname)
  973. {
  974. int i,ii=0;
  975. DWORD retval=0,
  976. dwSessions=0,
  977. dwValidSessions,
  978. dwOwnedSessions;
  979. PWTS_SESSION_INFO pSessionInfo=NULL;
  980. DWORD dwRetSize = 0;
  981. WINSTATIONINFORMATION WSInfo;
  982. /* param validation */
  983. if (!pUserSID || !pUserTbl || !pEntryCnt)
  984. {
  985. IMPORTANT_MSG((L"GetUserSessions parameter violation"));
  986. return 0;
  987. }
  988. /* Initialize */
  989. *pUserTbl = NULL;
  990. /* Start with WTSEnumerateSessions,
  991. * narrow it down to only active sessions,
  992. * then filter further against the logonID
  993. * then use WinStationQueryInformation to get the logged on users token
  994. */
  995. if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &dwSessions) ||
  996. !pSessionInfo || !dwSessions)
  997. {
  998. /* if we can't do this, then we must give up */
  999. IMPORTANT_MSG((L"GetUserSessions parameter violation"));
  1000. return 0;
  1001. }
  1002. /*
  1003. * Narrow down to only active sessions -
  1004. * a quick test
  1005. */
  1006. for (i=0,dwValidSessions=0;i<(int)dwSessions;i++)
  1007. {
  1008. if (pSessionInfo[i].State == WTSActive)
  1009. dwValidSessions++;
  1010. }
  1011. /* bail out early if none found */
  1012. if (dwValidSessions == 0)
  1013. {
  1014. /* free the memory we asked for */
  1015. WTSFreeMemory(pSessionInfo);
  1016. INTERESTING_MSG((L"No active sessions found"));
  1017. return 0;
  1018. }
  1019. INTERESTING_MSG((L"%d sessions found", dwValidSessions));
  1020. /*
  1021. * Now see who owns each session. Only one problem- session details kept
  1022. * by WTS have no concept of SIDs- just a user name & domain. Oh well,
  1023. * it is a safe assumption that the two are just as reliable forms of
  1024. * identification. One little caveat- the names can be stored using
  1025. * different case variants. Example: "TomFr" and "tomfr" are equivalent,
  1026. * according to NT domain rules, so I have to use a case-insensitive
  1027. * check here.
  1028. *
  1029. * So, now that I know there is at least one possible WTS Session here,
  1030. * I will compare these IDs to determine if we want to notify this particular
  1031. * session.
  1032. */
  1033. /* now look at all the active sessions and compare domain & user names */
  1034. for (i=0,dwOwnedSessions=0;i<(int)dwSessions;i++)
  1035. {
  1036. if (pSessionInfo[i].State == WTSActive)
  1037. {
  1038. DWORD CurSessionId = pSessionInfo[i].SessionId;
  1039. BOOL bLockedState=FALSE;
  1040. memset( &WSInfo, 0, sizeof(WSInfo) );
  1041. if (!WinStationQueryInformationW(
  1042. SERVERNAME_CURRENT,
  1043. CurSessionId,
  1044. WinStationInformation,
  1045. &WSInfo,
  1046. sizeof(WSInfo),
  1047. &dwRetSize
  1048. ))
  1049. {
  1050. IMPORTANT_MSG((L"WinStationQueryInformation failed err=0x%x", GetLastError()));
  1051. // SECURITY : if bad user's list is infront of good novice and
  1052. // bad user just happen to logoff at the right time, it will
  1053. // cause code to break out of loop instead of looking further
  1054. // for the intended novice.
  1055. continue;
  1056. }
  1057. if (StrCmpI(WSInfo.Domain, pDomainname))
  1058. continue;
  1059. if (StrCmpI(WSInfo.UserName, pUsername))
  1060. continue;
  1061. if (!WinStationQueryInformationW(
  1062. SERVERNAME_CURRENT,
  1063. CurSessionId,
  1064. WinStationLockedState,
  1065. &bLockedState,
  1066. sizeof(bLockedState),
  1067. &dwRetSize
  1068. ))
  1069. {
  1070. IMPORTANT_MSG((L"WinStationQueryInformation failed err=0x%x", GetLastError()));
  1071. break;
  1072. }
  1073. // if the screen is locked, don't add to the list
  1074. if (bLockedState)
  1075. continue;
  1076. INTERESTING_MSG((L"WinStaQI[%d]: ConnectState=0x%x bLockedState=0x%x user=%s/%s ", i,WSInfo.ConnectState, bLockedState, WSInfo.Domain,WSInfo.UserName));
  1077. /*
  1078. * If we got this far, then we know we want this session
  1079. */
  1080. dwOwnedSessions++;
  1081. // mark this as a "session of interest"
  1082. pSessionInfo[i].State = (WTS_CONNECTSTATE_CLASS)0x45;
  1083. }
  1084. }
  1085. /* did we find any sessions? */
  1086. if (dwOwnedSessions == 0)
  1087. {
  1088. TRIVIAL_MSG((L"No matching sessions found"));
  1089. goto none_found;
  1090. }
  1091. /* Get some memory for our session tables */
  1092. *pUserTbl = (PWTS_USER_SESSION_INFO)LocalAlloc(LMEM_FIXED, dwOwnedSessions * sizeof(WTS_USER_SESSION_INFO));
  1093. if (!*pUserTbl)
  1094. {
  1095. HEINOUS_E_MSG((L"Could not allocate memory for %d sessions, err=0x%x", dwOwnedSessions, GetLastError()));
  1096. goto none_found;
  1097. }
  1098. *pEntryCnt = dwOwnedSessions;
  1099. for (i=0,ii=0;i<(int)dwSessions; i++)
  1100. {
  1101. /*
  1102. * If this is one of our "sessions of interest", get the session ID
  1103. * and User Token.
  1104. */
  1105. if (pSessionInfo[i].State == (WTS_CONNECTSTATE_CLASS)0x45)
  1106. {
  1107. WINSTATIONUSERTOKEN WsUserToken;
  1108. ULONG ulRet;
  1109. PWTS_USER_SESSION_INFO lpi = &((*pUserTbl)[ii]);
  1110. WsUserToken.ProcessId = LongToHandle(GetCurrentProcessId());
  1111. WsUserToken.ThreadId = LongToHandle(GetCurrentThreadId());
  1112. lpi->dwIndex = (DWORD)ii;
  1113. lpi->dwSessionId = pSessionInfo[i].SessionId;
  1114. if (!WinStationQueryInformationW(WTS_CURRENT_SERVER_HANDLE, pSessionInfo[i].SessionId,
  1115. WinStationUserToken, &WsUserToken, sizeof(WsUserToken), &ulRet))
  1116. {
  1117. goto none_found;
  1118. }
  1119. lpi->hUserToken = WsUserToken.UserToken;
  1120. ii++;
  1121. }
  1122. }
  1123. /* Yahoo! we finally built our table. Now we can leave after cleaning up */
  1124. WTSFreeMemory(pSessionInfo);
  1125. IMPORTANT_MSG((L"GetUserSessions exiting: %d sessions found", *pEntryCnt));
  1126. return 1;
  1127. none_found:
  1128. /* free the memory we asked for */
  1129. if (pSessionInfo) WTSFreeMemory(pSessionInfo);
  1130. // we found no entries
  1131. *pEntryCnt =0;
  1132. if( *pUserTbl != NULL )
  1133. {
  1134. LocalFree( *pUserTbl );
  1135. *pUserTbl = NULL;
  1136. }
  1137. IMPORTANT_MSG((L"GetUserSessions exiting: no sessions found"));
  1138. return 1;
  1139. }
  1140. LPTHREAD_START_ROUTINE getKillProc(void)
  1141. {
  1142. LPTHREAD_START_ROUTINE lpKill = NULL;
  1143. HMODULE hKernel = LoadLibrary(L"kernel32");
  1144. if (hKernel)
  1145. {
  1146. lpKill = (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel, "ExitProcess");
  1147. FreeLibrary(hKernel);
  1148. }
  1149. return lpKill;
  1150. }
  1151. /*************************************************************
  1152. *
  1153. * localKill(WTS_USER_SESSION_INFO *SessInfo, LPTHREAD_START_ROUTINE lpKill)
  1154. * kills the process for us.
  1155. *
  1156. *************************************************************/
  1157. DWORD localKill(WTS_USER_SESSION_INFO *SessInfo, LPTHREAD_START_ROUTINE lpKill, LPSECURITY_ATTRIBUTES lpSA)
  1158. {
  1159. TRIVIAL_MSG((L"Entered localKill(0x%x, 0x%x)", SessInfo, lpKill));
  1160. if (lpKill)
  1161. {
  1162. HANDLE hKillThrd= CreateRemoteThread(
  1163. SessInfo->hProcess,
  1164. lpSA,
  1165. NULL,
  1166. lpKill,
  1167. 0,
  1168. 0,
  1169. NULL);
  1170. if (hKillThrd)
  1171. {
  1172. CloseHandle(hKillThrd);
  1173. return 1;
  1174. }
  1175. IMPORTANT_MSG((L"CreateRemoteThread failed. Err: %08x", GetLastError()));
  1176. // the fall-through is by design...
  1177. }
  1178. if(!TerminateProcess( SessInfo->hProcess, 0 ))
  1179. {
  1180. IMPORTANT_MSG((L"TerminateProcess failed. Err: %08x", GetLastError()));
  1181. }
  1182. return 0;
  1183. }
  1184. /*************************************************************
  1185. *
  1186. * getUserName()
  1187. * get the user's name & domain.
  1188. *
  1189. *************************************************************/
  1190. DWORD getUserName(PSID pUserSID, WCHAR **pUsername, WCHAR **pDomainname)
  1191. {
  1192. if (!pUsername || !pDomainname || !pUserSID)
  1193. return 0;
  1194. DWORD dwUsername=0, dwDomainname=0;
  1195. SID_NAME_USE eUse;
  1196. TRIVIAL_MSG((L"Entered getUserName"));
  1197. *pUsername=NULL, *pDomainname=NULL;
  1198. /* get the size of the buffers we will need */
  1199. if (!LookupAccountSid(NULL, pUserSID, NULL, &dwUsername, NULL, &dwDomainname, &eUse) &&
  1200. (!dwUsername || !dwDomainname))
  1201. {
  1202. IMPORTANT_MSG((L"LookupAccountSid(nulls) failed err=0x%x", GetLastError()));
  1203. return 0;
  1204. }
  1205. /* now get enough memory for our name & domain strings */
  1206. *pUsername = (WCHAR *)LocalAlloc(LMEM_FIXED, (dwUsername+16) * sizeof(WCHAR));
  1207. *pDomainname = (WCHAR *)LocalAlloc(LMEM_FIXED, (dwDomainname+16) * sizeof(WCHAR));
  1208. if (!*pUsername || !*pDomainname ||
  1209. !LookupAccountSid(NULL, pUserSID, *pUsername, &dwUsername, *pDomainname, &dwDomainname, &eUse))
  1210. {
  1211. /* if we get here, it is very bad!! */
  1212. DWORD error = GetLastError();
  1213. if (!*pUsername || !*pDomainname)
  1214. IMPORTANT_MSG((L"LocalAlloc failed err=0x%x", error));
  1215. else
  1216. IMPORTANT_MSG((L"LookupAccountSid(ptrs) failed err=0x%x", error));
  1217. return 0;
  1218. }
  1219. TRIVIAL_MSG((L"Requested user=[%ws] dom=[%ws]", *pUsername, *pDomainname));
  1220. return 1;
  1221. }
  1222. /*************************************************************
  1223. *
  1224. * ListFind(PSPLASHLIST pSplash, PSID user)
  1225. * returns TRUE if SID is found in our "splash table"
  1226. *
  1227. *************************************************************/
  1228. BOOL ListFind(PSPLASHLIST pSplash, PSID user)
  1229. {
  1230. PSPLASHLIST lpSplash;
  1231. if (!pSplash)
  1232. {
  1233. IMPORTANT_MSG((L"ListFind: no splash table memory"));
  1234. return FALSE;
  1235. }
  1236. if (!user)
  1237. {
  1238. IMPORTANT_MSG((L"Fatal error: no SID pointer"));
  1239. return FALSE;
  1240. }
  1241. lpSplash = pSplash;
  1242. while (lpSplash)
  1243. {
  1244. if (lpSplash->refcount &&
  1245. EqualSid(user, &lpSplash->Sid))
  1246. {
  1247. return TRUE;
  1248. }
  1249. lpSplash = (PSPLASHLIST) lpSplash->next;
  1250. }
  1251. return FALSE;
  1252. }
  1253. /*************************************************************
  1254. *
  1255. * ListInsert(PSPLASHLIST pSplash, int iSplashCnt, PSID user)
  1256. * inserts SID in our "splash table"
  1257. *
  1258. * NOTES:
  1259. * if already there, we increment the refcount for SID
  1260. *
  1261. *************************************************************/
  1262. BOOL ListInsert(PSPLASHLIST pSplash, PSID user)
  1263. {
  1264. PSPLASHLIST lpSplash;
  1265. if (!pSplash)
  1266. {
  1267. IMPORTANT_MSG((L"Fatal error: no splash table memory"));
  1268. return FALSE;
  1269. }
  1270. if (!user)
  1271. {
  1272. IMPORTANT_MSG((L"Fatal error: no SID pointer"));
  1273. return FALSE;
  1274. }
  1275. if (!IsValidSid(user))
  1276. {
  1277. IMPORTANT_MSG((L"Fatal error: bad SID"));
  1278. return FALSE;
  1279. }
  1280. // first, look for one already in the list
  1281. lpSplash = pSplash;
  1282. while (lpSplash)
  1283. {
  1284. if (lpSplash->refcount && EqualSid(user, &lpSplash->Sid))
  1285. {
  1286. TRIVIAL_MSG((L"Recycling SID in ListInsert"));
  1287. lpSplash->refcount++;
  1288. return TRUE;
  1289. }
  1290. lpSplash = (PSPLASHLIST) lpSplash->next;
  1291. }
  1292. // since we did not find one, let's add a new one
  1293. lpSplash = pSplash;
  1294. while (lpSplash->next)
  1295. lpSplash = (PSPLASHLIST) lpSplash->next;
  1296. int iSidSz = GetLengthSid(user);
  1297. lpSplash->next = LocalAlloc(LMEM_FIXED, sizeof(SPLASHLIST) + iSidSz - sizeof(SID));
  1298. if (lpSplash->next)
  1299. {
  1300. lpSplash = (PSPLASHLIST) lpSplash->next;
  1301. CopySid(iSidSz, &lpSplash->Sid, user);
  1302. lpSplash->refcount=1;
  1303. lpSplash->next = NULL;
  1304. return TRUE;
  1305. }
  1306. else
  1307. {
  1308. IMPORTANT_MSG((L"Fatal error: no memory available to extend splash tables"));
  1309. }
  1310. return FALSE;
  1311. }
  1312. /*************************************************************
  1313. *
  1314. * ListDelete(PSPLASHLIST pSplash, PSID user)
  1315. * deletes SID from our "splash table"
  1316. *
  1317. * NOTES:
  1318. * if there, we decrement the refcount for SID
  1319. * when refcount hits zero, SID is removed
  1320. *
  1321. *************************************************************/
  1322. BOOL ListDelete(PSPLASHLIST pSplash, PSID user)
  1323. {
  1324. PSPLASHLIST lpSplash, lpLast;
  1325. if (!pSplash)
  1326. {
  1327. IMPORTANT_MSG((L"Fatal error: no splash table memory"));
  1328. return FALSE;
  1329. }
  1330. if (!user)
  1331. {
  1332. IMPORTANT_MSG((L"Fatal error: no SID pointer"));
  1333. return FALSE;
  1334. }
  1335. lpSplash = lpLast = pSplash;
  1336. while (lpSplash)
  1337. {
  1338. if (lpSplash->refcount &&
  1339. EqualSid(user, &lpSplash->Sid))
  1340. {
  1341. lpSplash->refcount--;
  1342. TRIVIAL_MSG((L"found SID in ListDelete"));
  1343. if (!lpSplash->refcount)
  1344. {
  1345. // blow away the SID data
  1346. TRIVIAL_MSG((L"deleting SID entry"));
  1347. lpLast->next = lpSplash->next;
  1348. LocalFree(lpSplash);
  1349. }
  1350. return TRUE;
  1351. }
  1352. lpLast = lpSplash;
  1353. lpSplash = (PSPLASHLIST) lpSplash->next;
  1354. }
  1355. IMPORTANT_MSG((L"Fatal error: attempted to delete non-existant SID from splash table memory"));
  1356. return FALSE;
  1357. }
  1358. /*************************************************************
  1359. *
  1360. * DbgSpew(DbgClass, char *, ...)
  1361. * Sends debug information.
  1362. *
  1363. *************************************************************/
  1364. void DbgSpew(int DbgClass, BSTR lpFormat, va_list ap)
  1365. {
  1366. WCHAR szMessage[2400+3]; // extra space for '\r', '\n' and NULL
  1367. DWORD bufSize = sizeof(szMessage)/sizeof(szMessage[0]) - 3;
  1368. int iLen=0;
  1369. DWORD dwTick;
  1370. int seconds, millisec;
  1371. memset( szMessage, 0, sizeof(szMessage) );
  1372. if (gDbgFlag & DBG_MSG_TIME_MSGS)
  1373. {
  1374. dwTick = GetTickCount();
  1375. seconds = (int) dwTick / 1000;
  1376. millisec = (int) dwTick % 1000;
  1377. _snwprintf( szMessage, bufSize, L"%06d.%03d- ", seconds, millisec);
  1378. iLen = lstrlen(szMessage);
  1379. }
  1380. if ((DbgClass & 0x0F) >= (gDbgFlag & 0x0F))
  1381. {
  1382. // bufSize does not include three extra characters for L"\r\n\0"
  1383. if( (int)bufSize > iLen )
  1384. {
  1385. _vsnwprintf(szMessage+iLen, bufSize - iLen, lpFormat, ap);
  1386. }
  1387. wcscat(szMessage, L"\r\n");
  1388. // should this be sent to the debugger?
  1389. if (DbgClass & DBG_MSG_DEST_DBG)
  1390. OutputDebugStringW(szMessage);
  1391. // should this go to our log file?
  1392. if (iDbgFileHandle)
  1393. _write(iDbgFileHandle, szMessage, (2*lstrlen(szMessage)));
  1394. }
  1395. // should this msg go to the Event Logs?
  1396. if (DbgClass & DBG_MSG_DEST_EVENT)
  1397. {
  1398. WORD wType;
  1399. DWORD dwEvent;
  1400. if (DbgClass & DBG_MSG_CLASS_SECURE)
  1401. {
  1402. if (DbgClass & DBG_MSG_CLASS_ERROR)
  1403. {
  1404. wType = EVENTLOG_AUDIT_FAILURE;
  1405. dwEvent = SESSRSLR_E_SECURE;
  1406. }
  1407. else
  1408. {
  1409. wType = EVENTLOG_AUDIT_SUCCESS;
  1410. dwEvent = SESSRSLR_I_SECURE;
  1411. }
  1412. }
  1413. else
  1414. {
  1415. if (DbgClass & DBG_MSG_CLASS_ERROR)
  1416. {
  1417. wType = EVENTLOG_ERROR_TYPE;
  1418. dwEvent = SESSRSLR_E_GENERAL;
  1419. }
  1420. else
  1421. {
  1422. wType = EVENTLOG_INFORMATION_TYPE;
  1423. dwEvent = SESSRSLR_I_GENERAL;
  1424. }
  1425. }
  1426. /* write out an NT Event */
  1427. HANDLE hEvent = RegisterEventSource(NULL, MODULE_NAME);
  1428. LPCWSTR ArgsArray[1]={szMessage};
  1429. if (hEvent)
  1430. {
  1431. ReportEvent(hEvent, wType,
  1432. 0,
  1433. dwEvent,
  1434. NULL,
  1435. 1,
  1436. 0,
  1437. ArgsArray,
  1438. NULL);
  1439. DeregisterEventSource(hEvent);
  1440. }
  1441. }
  1442. }
  1443. void TrivialSpew(BSTR lpFormat, ...)
  1444. {
  1445. va_list vd;
  1446. va_start(vd, lpFormat);
  1447. DbgSpew(DBG_MSG_TRIVIAL+DBG_MSG_DEST_DBG, lpFormat, vd);
  1448. va_end(vd);
  1449. }
  1450. void InterestingSpew(BSTR lpFormat, ...)
  1451. {
  1452. va_list ap;
  1453. va_start(ap, lpFormat);
  1454. DbgSpew(DBG_MSG_INTERESTING+DBG_MSG_DEST_DBG, lpFormat, ap);
  1455. va_end(ap);
  1456. }
  1457. void ImportantSpew(BSTR lpFormat, ...)
  1458. {
  1459. va_list ap;
  1460. va_start(ap, lpFormat);
  1461. DbgSpew(DBG_MSG_IMPORTANT+DBG_MSG_DEST_DBG+DBG_MSG_DEST_FILE, lpFormat, ap);
  1462. va_end(ap);
  1463. }
  1464. void HeinousESpew(BSTR lpFormat, ...)
  1465. {
  1466. va_list ap;
  1467. va_start(ap, lpFormat);
  1468. DbgSpew(DBG_MSG_HEINOUS+DBG_MSG_DEST_DBG+DBG_MSG_DEST_FILE+DBG_MSG_DEST_EVENT+DBG_MSG_CLASS_ERROR, lpFormat, ap);
  1469. va_end(ap);
  1470. }
  1471. void HeinousISpew(BSTR lpFormat, ...)
  1472. {
  1473. va_list ap;
  1474. va_start(ap, lpFormat);
  1475. DbgSpew(DBG_MSG_HEINOUS+DBG_MSG_DEST_DBG+DBG_MSG_DEST_FILE+DBG_MSG_DEST_EVENT, lpFormat, ap);
  1476. va_end(ap);
  1477. }
  1478. // Blob format: propertylength;propertyName=value.
  1479. BOOL GetPropertyValueFromBlob(BSTR bstrHelpBlob, WCHAR * pName, WCHAR** ppValue)
  1480. {
  1481. WCHAR *p1, *p2, *pEnd;
  1482. BOOL bRet = FALSE;
  1483. LONG lTotal =0;
  1484. size_t lProp = 0;
  1485. size_t iNameLen;
  1486. if (!bstrHelpBlob || *bstrHelpBlob==L'\0' || !pName || *pName ==L'\0'|| !ppValue)
  1487. return FALSE;
  1488. iNameLen = wcslen(pName);
  1489. pEnd = bstrHelpBlob + wcslen(bstrHelpBlob);
  1490. p1 = p2 = bstrHelpBlob;
  1491. while (1)
  1492. {
  1493. // get property length
  1494. while (*p2 != L';' && *p2 != L'\0' && iswdigit(*p2) ) p2++;
  1495. if (*p2 != L';')
  1496. goto done;
  1497. *p2 = L'\0'; // set it to get length
  1498. lProp = _wtol(p1);
  1499. *p2 = L';'; // revert it back.
  1500. // security fix: In the case where we get a malformed header for
  1501. // the HelpBlob - exit
  1502. if (lProp <= iNameLen)
  1503. goto done;
  1504. // get property string
  1505. p1 = ++p2;
  1506. while (*p2 != L'=' && *p2 != L'\0' && p2 < p1+lProp) p2++;
  1507. if (*p2 != L'=')
  1508. goto done;
  1509. if (wcsncmp(p1, pName, iNameLen) == 0)
  1510. {
  1511. if (lProp == iNameLen+1) // A=B= case (no value)
  1512. goto done;
  1513. *ppValue = (WCHAR*)LocalAlloc(LMEM_FIXED, sizeof(WCHAR) * (lProp-iNameLen));
  1514. if (*ppValue == NULL)
  1515. goto done;
  1516. wcsncpy(*ppValue, p2+1, lProp-iNameLen-1);
  1517. (*ppValue)[lProp-iNameLen-1]=L'\0';
  1518. bRet = TRUE;
  1519. break;
  1520. }
  1521. // check next property
  1522. p2 = p1 = p1 + lProp;
  1523. if (p2 > pEnd)
  1524. break;
  1525. }
  1526. done:
  1527. return bRet;
  1528. }