Source code of Windows XP (NT5)
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.

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