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.

1152 lines
35 KiB

  1. /******************************************************************************
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. erwait.cpp
  5. Revision History:
  6. derekm 02/28/2001 created
  7. ******************************************************************************/
  8. #include "stdafx.h"
  9. #include <stdio.h>
  10. #include <pfrcfg.h>
  11. // this is a private NT function that doesn't appear to be defined anywhere
  12. // public, so I'm including it here
  13. extern "C"
  14. {
  15. HANDLE GetCurrentUserTokenW( WCHAR Winsta[], DWORD DesiredAccess);
  16. }
  17. //////////////////////////////////////////////////////////////////////////////
  18. // utility functions
  19. // ***************************************************************************
  20. BOOL CreateQueueDir(void)
  21. {
  22. SECURITY_DESCRIPTOR sd;
  23. SECURITY_ATTRIBUTES sa;
  24. WCHAR wszDir[MAX_PATH], *pwszDir = NULL;
  25. DWORD dw, cchNeed, cch;
  26. BOOL fRet = FALSE;
  27. USE_TRACING("CreateQueueDir");
  28. ZeroMemory(&sa, sizeof(sa));
  29. ZeroMemory(&sd, sizeof(sd));
  30. if (AllocSD(&sd, DIR_ACCESS_ALL, DIR_ACCESS_ALL, 0) == FALSE)
  31. {
  32. DBG_MSG("AllocSD dies");
  33. goto done;
  34. }
  35. sa.nLength = sizeof(sa);
  36. sa.bInheritHandle = FALSE;
  37. sa.lpSecurityDescriptor = &sd;
  38. cch = GetSystemWindowsDirectoryW(wszDir, sizeofSTRW(wszDir));
  39. if (cch == 0)
  40. {
  41. DBG_MSG("GetSystemWindowsDirectoryW died");
  42. goto done;
  43. }
  44. cchNeed = cch + sizeofSTRW(c_wszQSubdir) + 2;
  45. if (cchNeed > sizeofSTRW(wszDir))
  46. {
  47. __try { pwszDir = (WCHAR *)_alloca(cchNeed * sizeof(WCHAR)); }
  48. __except(EXCEPTION_STACK_OVERFLOW) { pwszDir = NULL; }
  49. if (pwszDir == NULL)
  50. {
  51. SetLastError(ERROR_OUTOFMEMORY);
  52. goto done;
  53. }
  54. if (cch > sizeofSTRW(wszDir))
  55. cch = GetSystemWindowsDirectoryW(pwszDir, cchNeed);
  56. else
  57. wcscpy(pwszDir, wszDir);
  58. }
  59. else
  60. {
  61. pwszDir = wszDir;
  62. }
  63. // if the length of the system directory is 3, then %windir% is in the form
  64. // of X:\, so clip off the backslash so we don't have to special case
  65. // it below...
  66. if (cch == 3)
  67. *(pwszDir + 2) = L'\0';
  68. wcscat(pwszDir, L"\\PCHealth");
  69. if (CreateDirectoryW(pwszDir, NULL) == FALSE &&
  70. GetLastError() != ERROR_ALREADY_EXISTS)
  71. {
  72. DBG_MSG("Can't make dir1");
  73. goto done;
  74. }
  75. wcscat(pwszDir, L"\\ErrorRep");
  76. if (CreateDirectoryW(pwszDir, NULL) == FALSE &&
  77. GetLastError() != ERROR_ALREADY_EXISTS)
  78. {
  79. DBG_MSG("Can't make dir2");
  80. goto done;
  81. }
  82. wcscat(pwszDir, L"\\UserDumps");
  83. if (CreateDirectoryW(pwszDir, &sa) == FALSE &&
  84. GetLastError() != ERROR_ALREADY_EXISTS)
  85. {
  86. DBG_MSG("Can't make dir3");
  87. goto done;
  88. }
  89. // if the directory exists, then we need to write the security
  90. // descriptor to it cuz we want to make sure that no inappropriate
  91. // people can access it.
  92. if (GetLastError() == ERROR_ALREADY_EXISTS)
  93. {
  94. SID_IDENTIFIER_AUTHORITY siaNT = SECURITY_NT_AUTHORITY;
  95. PACL pacl = NULL;
  96. PSID psidLS = NULL;
  97. BOOL fDef, fACL;
  98. if (GetSecurityDescriptorDacl(&sd, &fACL, &pacl, &fDef) == FALSE)
  99. goto done;
  100. if (AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0,
  101. 0, 0, 0, 0, 0, 0, &psidLS) == FALSE)
  102. goto done;
  103. dw = SetNamedSecurityInfoW(pwszDir, SE_FILE_OBJECT,
  104. DACL_SECURITY_INFORMATION |
  105. OWNER_SECURITY_INFORMATION |
  106. PROTECTED_DACL_SECURITY_INFORMATION,
  107. psidLS, NULL, pacl, NULL);
  108. FreeSid(psidLS);
  109. if (dw != ERROR_SUCCESS)
  110. {
  111. SetLastError(dw);
  112. goto done;
  113. }
  114. }
  115. fRet = TRUE;
  116. done:
  117. dw = GetLastError();
  118. if (sa.lpSecurityDescriptor != NULL)
  119. FreeSD((SECURITY_DESCRIPTOR *)sa.lpSecurityDescriptor);
  120. SetLastError(dw);
  121. DBG_MSG(fRet ? "OK" : "failed");
  122. return fRet;
  123. }
  124. // ***************************************************************************
  125. HMODULE LoadERDll(void)
  126. {
  127. HMODULE hmod = NULL;
  128. WCHAR wszMod[MAX_PATH], *pwszMod;
  129. DWORD cch, cchNeed;
  130. cch = GetSystemDirectoryW(wszMod, sizeofSTRW(wszMod));
  131. if (cch == 0)
  132. goto done;
  133. // the '14' is for the
  134. cchNeed = cch + 14;
  135. if (cchNeed > sizeofSTRW(wszMod))
  136. {
  137. __try { pwszMod = (WCHAR *)_alloca(cchNeed * sizeof(WCHAR)); }
  138. __except(EXCEPTION_STACK_OVERFLOW) { pwszMod = NULL; }
  139. if (pwszMod == NULL)
  140. {
  141. SetLastError(ERROR_OUTOFMEMORY);
  142. goto done;
  143. }
  144. if (cch > sizeofSTRW(wszMod))
  145. cch = GetSystemDirectoryW(pwszMod, cchNeed);
  146. else
  147. wcscpy(pwszMod, wszMod);
  148. }
  149. else
  150. {
  151. pwszMod = wszMod;
  152. }
  153. // if the length of the system directory is 3, then %windir% is in the form
  154. // of X:\, so clip off the backslash so we don't have to special case
  155. // it below...
  156. if (cch == 3)
  157. *(pwszMod + 2) = L'\0';
  158. wcscat(pwszMod, L"\\faultrep.dll");
  159. hmod = LoadLibraryExW(wszMod, NULL, 0);
  160. done:
  161. return hmod;
  162. }
  163. // **************************************************************************
  164. BOOL FindAdminSession(DWORD *pdwSession, HANDLE *phToken)
  165. {
  166. USE_TRACING("FindAdminSession");
  167. WINSTATIONUSERTOKEN wsut;
  168. LOGONIDW *rgSesn = NULL;
  169. DWORD i, cSesn, cb, dw;
  170. BOOL fRet = FALSE;
  171. HRESULT hr = NOERROR;
  172. ZeroMemory(&wsut, sizeof(wsut));
  173. VALIDATEPARM(hr, (pdwSession == NULL || phToken == NULL));
  174. if (FAILED(hr))
  175. {
  176. SetLastError(ERROR_INVALID_PARAMETER);
  177. goto done;
  178. }
  179. *pdwSession = (DWORD)-1;
  180. *phToken = NULL;
  181. fRet = WinStationEnumerateW(SERVERNAME_CURRENT, &rgSesn, &cSesn);
  182. if (fRet == FALSE)
  183. goto done;
  184. wsut.ProcessId = LongToHandle(GetCurrentProcessId());
  185. wsut.ThreadId = LongToHandle(GetCurrentThreadId());
  186. for(i = 0; i < cSesn; i++)
  187. {
  188. if (rgSesn[i].State != State_Active)
  189. continue;
  190. fRet = WinStationQueryInformationW(SERVERNAME_CURRENT,
  191. rgSesn[i].SessionId,
  192. WinStationUserToken, &wsut,
  193. sizeof(wsut), &cb);
  194. if (fRet == FALSE)
  195. continue;
  196. if (wsut.UserToken != NULL)
  197. {
  198. if (IsUserAnAdmin(wsut.UserToken))
  199. break;
  200. CloseHandle(wsut.UserToken);
  201. wsut.UserToken = NULL;
  202. }
  203. }
  204. if (i < cSesn)
  205. {
  206. fRet = TRUE;
  207. *pdwSession = rgSesn[i].SessionId;
  208. *phToken = wsut.UserToken;
  209. }
  210. else
  211. {
  212. fRet = FALSE;
  213. }
  214. done:
  215. dw = GetLastError();
  216. if (rgSesn != NULL)
  217. WinStationFreeMemory(rgSesn);
  218. SetLastError(dw);
  219. return fRet;
  220. }
  221. // ***************************************************************************
  222. BOOL GetInteractiveUsersToken(HANDLE *phTokenUser)
  223. {
  224. HWINSTA hwinsta = NULL;
  225. DWORD cbNeed;
  226. BOOL fRet = FALSE;
  227. PSID psid = NULL;
  228. HRESULT hr = NOERROR;
  229. USE_TRACING("GetInteractiveUsersToken");
  230. VALIDATEPARM(hr, (phTokenUser == NULL));
  231. if (FAILED(hr))
  232. {
  233. SetLastError(ERROR_INVALID_PARAMETER);
  234. goto done;
  235. }
  236. *phTokenUser = NULL;
  237. hwinsta = OpenWindowStationW(L"WinSta0", FALSE, MAXIMUM_ALLOWED);
  238. if (hwinsta == NULL)
  239. goto done;
  240. // if this function returns 0 for cbNeed, there is no one logged in. Also,
  241. // it should never return TRUE with these parameters. If it does,
  242. // something's wrong...
  243. fRet = GetUserObjectInformationW(hwinsta, UOI_USER_SID, NULL, 0, &cbNeed);
  244. if (fRet || cbNeed == 0)
  245. {
  246. fRet = FALSE;
  247. goto done;
  248. }
  249. *phTokenUser = GetCurrentUserTokenW(L"WinSta0", TOKEN_ALL_ACCESS);
  250. fRet = (*phTokenUser != NULL);
  251. done:
  252. if (hwinsta != NULL)
  253. CloseWindowStation(hwinsta);
  254. return fRet;
  255. }
  256. // ***************************************************************************
  257. BOOL ValidateUserAccessToProcess(HANDLE hPipe, HANDLE hProcRemote)
  258. {
  259. PSECURITY_DESCRIPTOR psd = NULL;
  260. GENERIC_MAPPING gm;
  261. HRESULT hr = NOERROR;
  262. PRIVILEGE_SET *pPS;
  263. ACCESS_MASK amReq;
  264. HANDLE hTokenImp = NULL;
  265. DWORD dwErr = 0, cbPS, dwGranted;
  266. PACL pDACL = NULL;
  267. PACL pSACL = NULL;
  268. PSID psidOwner = NULL;
  269. PSID psidGroup = NULL;
  270. BYTE rgBuf[sizeof(PRIVILEGE_SET) + 3 * sizeof(LUID_AND_ATTRIBUTES)];
  271. BOOL fRet = FALSE, fStatus = FALSE;
  272. USE_TRACING("ValidateUserAccessToProcess");
  273. VALIDATEPARM(hr, (hPipe == NULL || hProcRemote == NULL));
  274. if (FAILED(hr))
  275. {
  276. SetLastError(ERROR_INVALID_PARAMETER);
  277. goto done;
  278. }
  279. ZeroMemory(&gm, sizeof(gm));
  280. gm.GenericAll = GENERIC_ALL;
  281. gm.GenericExecute = GENERIC_EXECUTE;
  282. gm.GenericRead = GENERIC_READ;
  283. gm.GenericWrite = GENERIC_WRITE;
  284. pPS = (PRIVILEGE_SET *)rgBuf;
  285. cbPS = sizeof(rgBuf);
  286. // get the SD for the remote process
  287. dwErr = GetSecurityInfo(hProcRemote, SE_KERNEL_OBJECT,
  288. DACL_SECURITY_INFORMATION |
  289. GROUP_SECURITY_INFORMATION |
  290. OWNER_SECURITY_INFORMATION |
  291. SACL_SECURITY_INFORMATION, &psidOwner, &psidGroup,
  292. &pDACL, &pSACL, &psd);
  293. if (dwErr != ERROR_SUCCESS)
  294. {
  295. DBG_MSG("Failed to get security info");
  296. SetLastError(dwErr);
  297. goto done;
  298. }
  299. // get the client's token
  300. fRet = ImpersonateNamedPipeClient(hPipe);
  301. if (fRet == FALSE)
  302. {
  303. DBG_MSG("Impersonate pipe failed");
  304. goto done;
  305. }
  306. fRet = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hTokenImp);
  307. // don't need to be the other user anymore, so go back to being LocalSystem
  308. RevertToSelf();
  309. if (fRet == FALSE)
  310. {
  311. DBG_MSG("OpenThreadToken failed");
  312. goto done;
  313. }
  314. amReq = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
  315. fRet = AccessCheck(psd, hTokenImp, amReq, &gm, pPS, &cbPS, &dwGranted,
  316. &fStatus);
  317. if (fRet == FALSE)
  318. {
  319. DBG_MSG("AccessCheck died");
  320. goto done;
  321. }
  322. if (fStatus == FALSE || (dwGranted & amReq) != amReq)
  323. {
  324. DBG_MSG("Bogus state");
  325. fRet = FALSE;
  326. SetLastError(ERROR_ACCESS_DENIED);
  327. goto done;
  328. }
  329. fRet = TRUE;
  330. done:
  331. if (hTokenImp != NULL)
  332. CloseHandle(hTokenImp);
  333. if (psd != NULL)
  334. LocalFree(psd);
  335. DBG_MSG(fRet ? "validated" : "NOT VALIDATED");
  336. return fRet;
  337. }
  338. //////////////////////////////////////////////////////////////////////////////
  339. // remote execution functions
  340. // ***************************************************************************
  341. BOOL ProcessFaultRequest(HANDLE hPipe, PBYTE pBuf, DWORD *pcbBuf)
  342. {
  343. SPCHExecServFaultRequest *pesreq = (SPCHExecServFaultRequest *)pBuf;
  344. SPCHExecServFaultReply esrep, *pesrep;
  345. OSVERSIONINFOEXW osvi;
  346. SFaultRepManifest frm;
  347. EFaultRepRetVal frrv = frrvErrNoDW;
  348. HMODULE hmodFR = NULL;
  349. LPWSTR wszTempDir = NULL, wszDumpFileName = NULL;
  350. HANDLE hprocRemote = NULL;
  351. HANDLE hTokenUser = NULL;
  352. LPVOID pvEnv = NULL;
  353. WCHAR *pwszDump = NULL;
  354. WCHAR wszTemp[MAX_PATH];
  355. DWORD cb, dw, dwSessionId;
  356. WCHAR wch = L'\0', *pwch, *pwszFile;
  357. BOOL fRet = FALSE, fUseManifest = FALSE;
  358. BOOL fQueue = FALSE, fTS = FALSE;
  359. HRESULT hr = NOERROR;
  360. USE_TRACING("ProcessFaultRequest");
  361. ZeroMemory(&esrep, sizeof(esrep));
  362. ZeroMemory(&frm, sizeof(frm));
  363. pwszDump = &wch;
  364. SetLastError(ERROR_INVALID_PARAMETER);
  365. esrep.cb = sizeof(esrep);
  366. esrep.ess = essErr;
  367. // validate parameters
  368. VALIDATEPARM(hr, (pesreq->cbTotal > *pcbBuf ||
  369. pesreq->cbTotal < sizeof(SPCHExecServFaultRequest) ||
  370. pesreq->cbESR != sizeof(SPCHExecServFaultRequest) ||
  371. pesreq->thidFault == 0 ||
  372. pesreq->wszExe == 0 ||
  373. pesreq->wszExe >= *pcbBuf));
  374. if (FAILED(hr))
  375. {
  376. SetLastError(ERROR_INVALID_PARAMETER);
  377. goto done;
  378. }
  379. // check and make sure that there is a NULL terminator between the
  380. // start of the string & the end of the buffer
  381. pwszFile = (LPWSTR)(pBuf + *pcbBuf);
  382. frm.wszExe = (LPWSTR)(pBuf + (DWORD)pesreq->wszExe);
  383. for (pwch = frm.wszExe; pwch < pwszFile && *pwch != L'\0'; pwch++);
  384. VALIDATEPARM(hr, (pwch >= pwszFile));
  385. if (FAILED(hr))
  386. {
  387. SetLastError(ERROR_INVALID_PARAMETER);
  388. goto done;
  389. }
  390. // need the filename for comparison later...
  391. for (pwszFile = pwch;
  392. pwszFile > frm.wszExe && *pwszFile != L'\\';
  393. pwszFile--);
  394. if (*pwszFile == L'\\')
  395. pwszFile++;
  396. frm.pidReqProcess = pesreq->pidReqProcess;
  397. frm.pvFaultAddr = pesreq->pvFaultAddr;
  398. frm.thidFault = pesreq->thidFault;
  399. frm.fIs64bit = pesreq->fIs64bit;
  400. frm.pEP = pesreq->pEP;
  401. ZeroMemory(&osvi, sizeof(osvi));
  402. osvi.dwOSVersionInfoSize = sizeof(osvi);
  403. GetVersionExW((LPOSVERSIONINFOW)&osvi);
  404. if ((osvi.wSuiteMask & (VER_SUITE_TERMINAL | VER_SUITE_SINGLEUSERTS)) != 0)
  405. fTS = TRUE;
  406. // if it's one of these processes, then we need to ALWAYS go queued.
  407. if (_wcsicmp(pwszFile, L"lsass.exe") == 0 ||
  408. _wcsicmp(pwszFile, L"winlogon.exe") == 0 ||
  409. _wcsicmp(pwszFile, L"csrss.exe") == 0 ||
  410. _wcsicmp(pwszFile, L"smss.exe") == 0)
  411. {
  412. fQueue = TRUE;
  413. fUseManifest = FALSE;
  414. }
  415. if (fQueue == FALSE)
  416. {
  417. // need the session id for the process
  418. fRet = ProcessIdToSessionId(pesreq->pidReqProcess, &dwSessionId);
  419. if (fTS && fRet)
  420. {
  421. WINSTATIONINFORMATIONW wsi;
  422. WINSTATIONUSERTOKEN wsut;
  423. ZeroMemory(&wsi, sizeof(wsi));
  424. fRet = WinStationQueryInformationW(SERVERNAME_CURRENT,
  425. dwSessionId,
  426. WinStationInformation,
  427. &wsi, sizeof(wsi), &cb);
  428. if (fRet == FALSE)
  429. {
  430. DBG_MSG("WinStaQI failed");
  431. goto doneTSTokenFetch;
  432. }
  433. // so the session isn't active. Determine what our session is cuz
  434. // if the faulting process and the ERSvc are in the same session,
  435. // then our session is obviously not active either. So it's not
  436. // useful to obtain the user token for it.
  437. if (wsi.ConnectState != State_Active)
  438. {
  439. DBG_MSG("TS session not active");
  440. fRet = FALSE;
  441. goto doneTokenFetch;
  442. }
  443. // get the token of the user we want to pop up the display to.
  444. // If this fn returns FALSE, assume there is no one logged
  445. // into our session & just go to delayed fault reporting
  446. ZeroMemory(&wsut, sizeof(wsut));
  447. wsut.ProcessId = LongToHandle(GetCurrentProcessId());
  448. wsut.ThreadId = LongToHandle(GetCurrentThreadId());
  449. fRet = WinStationQueryInformationW(SERVERNAME_CURRENT,
  450. dwSessionId,
  451. WinStationUserToken, &wsut,
  452. sizeof(wsut), &cb);
  453. if (fRet == FALSE)
  454. {
  455. DBG_MSG("nobody logged in");
  456. goto doneTSTokenFetch;
  457. }
  458. hTokenUser = wsut.UserToken;
  459. }
  460. else
  461. {
  462. fRet = FALSE;
  463. }
  464. doneTSTokenFetch:
  465. if (fRet == FALSE)
  466. fRet = GetInteractiveUsersToken(&hTokenUser);
  467. // if the above call succeeded, check if the user is an admin or not...
  468. // If he is, we can just display DW directly. Otherwise, we go into
  469. // delayed reporting mode.
  470. if (fRet && hTokenUser != NULL)
  471. fUseManifest = IsUserAnAdmin(hTokenUser);
  472. }
  473. doneTokenFetch:
  474. // if we are going to go into manifest mode, then check to see if we really
  475. // need to go into Q mode
  476. if (!fQueue)
  477. {
  478. EEnDis eedReport;
  479. HKEY hkey = NULL;
  480. // first check the policy key
  481. dw = RegOpenKeyExW(HKEY_LOCAL_MACHINE, c_wszRPCfgPolicy, 0, KEY_READ,
  482. &hkey);
  483. if (dw == ERROR_SUCCESS)
  484. {
  485. // ok, if that succeeded then check and see if the DoReport value
  486. // is here...
  487. cb = sizeof(eedReport);
  488. dw = RegQueryValueExW(hkey, c_wszRVDoReport, 0, NULL,
  489. (LPBYTE)&eedReport, &cb);
  490. if (dw == ERROR_SUCCESS)
  491. {
  492. cb = sizeof(fQueue);
  493. dw = RegQueryValueExW(hkey, c_wszRVForceQueue, 0, NULL,
  494. (LPBYTE)&fQueue, &cb);
  495. // if it's not a valid value, then pretend we got an error
  496. if (dw == ERROR_SUCCESS && fQueue != TRUE && fQueue != FALSE)
  497. dw = ERROR_INVALID_PARAMETER;
  498. }
  499. else
  500. {
  501. RegCloseKey(hkey);
  502. hkey = NULL;
  503. }
  504. DBG_MSG(fQueue ? "Q=1 in cpl" : "Q = 0 in policy");
  505. }
  506. // if we didn't find a policy key or we were not able to read the
  507. // 'DoReport' value from it, then try the CPL key
  508. if (dw != ERROR_SUCCESS && hkey == NULL)
  509. {
  510. dw = RegOpenKeyExW(HKEY_LOCAL_MACHINE, c_wszRPCfg, 0, KEY_READ,
  511. &hkey);
  512. if (dw == ERROR_SUCCESS)
  513. {
  514. cb = sizeof(eedReport);
  515. dw = RegQueryValueExW(hkey, c_wszRVDoReport, 0, NULL,
  516. (LPBYTE)&eedReport, &cb);
  517. if (dw == ERROR_SUCCESS /*&& eedReport != eedDisabled*/)
  518. {
  519. cb = sizeof(fQueue);
  520. dw = RegQueryValueExW(hkey, c_wszRVForceQueue, 0, NULL,
  521. (LPBYTE)&fQueue, &cb);
  522. // if it's not a valid value, then pretend we got an error
  523. if (dw == ERROR_SUCCESS && fQueue != TRUE &&
  524. fQueue != FALSE)
  525. dw = ERROR_INVALID_PARAMETER;
  526. }
  527. }
  528. DBG_MSG(fQueue ? "Q=1 in cpl" : "Q = 0 in cpl");
  529. }
  530. // ok, if we still haven't got an ERROR_SUCCESS value back, then
  531. // determine what the default should be.
  532. if (dw != ERROR_SUCCESS)
  533. fQueue = (osvi.wProductType == VER_NT_SERVER);
  534. if (hkey != NULL)
  535. RegCloseKey(hkey);
  536. if (fQueue)
  537. fUseManifest = FALSE;
  538. }
  539. DBG_MSG(fQueue ? "Q=1" : "Q = 0");
  540. // ok, so if we're not in forced queue mode & we're not in manifest mode
  541. // then go hunt and see if any other session on the machine has an admin
  542. // logged into it.
  543. if (fQueue == FALSE && fUseManifest == FALSE && fTS)
  544. {
  545. if (hTokenUser != NULL)
  546. {
  547. CloseHandle(hTokenUser);
  548. hTokenUser = NULL;
  549. }
  550. fUseManifest = FindAdminSession(&dwSessionId, &hTokenUser);
  551. }
  552. hmodFR = LoadERDll();
  553. if (hmodFR == NULL)
  554. {
  555. DBG_MSG("LoadERDll failed");
  556. goto done;
  557. }
  558. // need a handle to the process to verify that the user has access to it.
  559. hprocRemote = OpenProcess(PROCESS_DUP_HANDLE |
  560. PROCESS_QUERY_INFORMATION |
  561. PROCESS_VM_READ |
  562. READ_CONTROL |
  563. ACCESS_SYSTEM_SECURITY,
  564. FALSE, pesreq->pidReqProcess);
  565. if (hprocRemote == NULL)
  566. {
  567. DBG_MSG("OpenProc failed");
  568. goto done;
  569. }
  570. fRet = ValidateUserAccessToProcess(hPipe, hprocRemote);
  571. if (fRet == FALSE)
  572. {
  573. DBG_MSG("not validated");
  574. goto done;
  575. }
  576. if (fUseManifest)
  577. {
  578. PROCESS_INFORMATION pi;
  579. pfn_REPORTFAULTDWM pfn;
  580. DWORD cch;
  581. DBG_MSG("manifest");
  582. ZeroMemory(&pi, sizeof(pi));
  583. pfn = (pfn_REPORTFAULTDWM)GetProcAddress(hmodFR, "ReportFaultDWM");
  584. if (pfn == NULL)
  585. {
  586. DBG_MSG("getProcAddr died");
  587. goto done;
  588. }
  589. // need to figure out the temp path for the logged on user...
  590. wszTemp[0] = L'\0';
  591. fRet = ExpandEnvironmentStringsForUserW(hTokenUser, L"%TMP%",
  592. wszTemp, sizeofSTRW(wszTemp));
  593. if (fRet == FALSE || wszTemp[0] == L'\0')
  594. {
  595. fRet = ExpandEnvironmentStringsForUserW(hTokenUser, L"%TEMP%",
  596. wszTemp, sizeofSTRW(wszTemp));
  597. if (fRet == FALSE || wszTemp[0] == L'\0')
  598. {
  599. fRet = ExpandEnvironmentStringsForUserW(hTokenUser, L"%USERPROFILE%",
  600. wszTemp, sizeofSTRW(wszTemp));
  601. if (fRet == FALSE || wszTemp[0] == L'\0')
  602. GetTempPathW(sizeofSTRW(wszTemp), wszTemp);
  603. }
  604. }
  605. // determine what the name of the dump file will be
  606. cch = wcslen(pwszFile) + sizeofSTRW(c_wszDumpSuffix) + 1;
  607. __try { wszDumpFileName = (WCHAR *)_alloca(cch * sizeof(WCHAR)); }
  608. __except(EXCEPTION_STACK_OVERFLOW) { wszDumpFileName = NULL; }
  609. if (wszDumpFileName == NULL)
  610. {
  611. SetLastError(ERROR_OUTOFMEMORY);
  612. goto done;
  613. }
  614. wcscpy(wszDumpFileName, pwszFile);
  615. wcscat(wszDumpFileName, c_wszDumpSuffix);
  616. // get a dir where we'll put all our temp files
  617. if (CreateTempDirAndFile(wszTemp, NULL, &wszTempDir) == 0)
  618. goto done;
  619. // need an environment block for the target user to properly launch DW
  620. if (CreateEnvironmentBlock(&pvEnv, hTokenUser, FALSE) == FALSE)
  621. pvEnv = NULL;
  622. // do the real work.
  623. frrv = (*pfn)(&frm, wszTempDir, hTokenUser, pvEnv, &pi,
  624. wszDumpFileName);
  625. // if we don't get this error code back, then we didn't launch the
  626. // process, or it died for some reason. Now we will try to queue it
  627. if (frrv != frrvOk)
  628. {
  629. fUseManifest = FALSE;
  630. goto try_queue;
  631. }
  632. // we only need to duplicate the hProcess back to the requesting process
  633. // cuz it doesn't use any of the other values in the PROCESSINFORMATION
  634. // structure.
  635. if (pi.hThread != NULL)
  636. CloseHandle(pi.hThread);
  637. if (hprocRemote != NULL)
  638. {
  639. fRet = DuplicateHandle(GetCurrentProcess(), pi.hProcess,
  640. hprocRemote, &esrep.hProcess, 0, FALSE,
  641. DUPLICATE_SAME_ACCESS);
  642. if (fRet == FALSE)
  643. esrep.hProcess = NULL;
  644. }
  645. if (pi.hProcess != NULL)
  646. CloseHandle(pi.hProcess);
  647. esrep.ess = essOk;
  648. }
  649. // since the user is NOT an admin, we have to queue it for later viewing
  650. // the next time an admin logs on
  651. else
  652. {
  653. pfn_REPORTFAULTTOQ pfn;
  654. try_queue:
  655. DBG_MSG("queue mode");
  656. pfn = (pfn_REPORTFAULTTOQ)GetProcAddress(hmodFR, "ReportFaultToQueue");
  657. if (pfn == NULL)
  658. {
  659. DBG_MSG("GetProcAddr died");
  660. goto done;
  661. }
  662. if (CreateQueueDir() == FALSE)
  663. {
  664. DBG_MSG("CreateQdir died");
  665. goto done;
  666. }
  667. frrv = (*pfn)(&frm);
  668. // want to be local system again...
  669. dw = GetLastError();
  670. RevertToSelf();
  671. SetLastError(dw);
  672. if (frrv != frrvOk)
  673. goto done;
  674. esrep.ess = essOkQueued;
  675. }
  676. SetLastError(0);
  677. fRet = TRUE;
  678. done:
  679. esrep.dwErr = GetLastError();
  680. // build the reply packet with the handle valid in the context
  681. // of the requesting process
  682. pesrep = (SPCHExecServFaultReply *)pBuf;
  683. RtlCopyMemory(pesrep, &esrep, sizeof(esrep));
  684. *pcbBuf = sizeof(esrep) + sizeof(esrep) % sizeof(WCHAR);
  685. if (fUseManifest)
  686. {
  687. pBuf += *pcbBuf;
  688. if (wszTempDir != NULL)
  689. {
  690. cb = (wcslen(wszTempDir) + 1) * sizeof(WCHAR);
  691. RtlCopyMemory(pBuf, wszTempDir, cb);
  692. }
  693. else
  694. {
  695. cb = sizeof(WCHAR);
  696. *pBuf = L'\0';
  697. }
  698. *pcbBuf += cb;
  699. pesrep->wszDir = (UINT64)pBuf - (UINT64)pesrep;
  700. pBuf += cb;
  701. if (wszDumpFileName != NULL)
  702. {
  703. cb = (wcslen(wszDumpFileName) + 1) * sizeof(WCHAR);
  704. RtlCopyMemory(pBuf, wszDumpFileName, cb);
  705. }
  706. else
  707. {
  708. cb = sizeof(WCHAR);
  709. *pBuf = L'\0';
  710. }
  711. *pcbBuf += cb;
  712. pesrep->wszDumpName = (UINT64)pBuf - (UINT64)pesrep;
  713. }
  714. if (wszTempDir != NULL)
  715. MyFree(wszTempDir);
  716. if (pvEnv != NULL)
  717. DestroyEnvironmentBlock(pvEnv);
  718. if (hprocRemote != NULL)
  719. CloseHandle(hprocRemote);
  720. if (hTokenUser != NULL)
  721. CloseHandle(hTokenUser);
  722. if (hmodFR != NULL)
  723. FreeLibrary(hmodFR);
  724. return fRet;
  725. }
  726. // ***************************************************************************
  727. // Note that the pipe that this thread services is secured such that only
  728. // local system has access to it. Thus, we do not need to impersonate the
  729. // pipe client in it.
  730. BOOL ProcessHangRequest(HANDLE hPipe, PBYTE pBuf, DWORD *pcbBuf)
  731. {
  732. SPCHExecServHangRequest *pesreq = (SPCHExecServHangRequest *)pBuf;
  733. SPCHExecServHangReply esrep;
  734. PROCESS_INFORMATION pi;
  735. WINSTATIONUSERTOKEN wsut;
  736. OSVERSIONINFOEXW osvi;
  737. STARTUPINFOW si;
  738. HANDLE hprocRemote = NULL, hTokenUser = NULL;
  739. LPVOID pvEnv = NULL;
  740. LPWSTR wszEventName;
  741. DWORD cbWrote, dwErr, cch, cchNeed;
  742. WCHAR wszSysDir[MAX_PATH], *pwszSysDir = NULL;
  743. WCHAR *pwszCmdLine = NULL, *pwszEnd = NULL;
  744. WCHAR *pwch;
  745. BOOL fRet = FALSE;
  746. HRESULT hr;
  747. USE_TRACING("ProcessHangRequest");
  748. ZeroMemory(&pi, sizeof(pi));
  749. ZeroMemory(&wsut, sizeof(wsut));
  750. ZeroMemory(&esrep, sizeof(esrep));
  751. SetLastError(ERROR_INVALID_PARAMETER);
  752. esrep.cb = sizeof(esrep);
  753. esrep.ess = essErr;
  754. // validate parameters
  755. VALIDATEPARM(hr, (*pcbBuf < sizeof(SPCHExecServHangRequest) ||
  756. pesreq->cbESR != sizeof(SPCHExecServHangRequest) ||
  757. pesreq->wszEventName == 0 ||
  758. pesreq->wszEventName >= *pcbBuf ||
  759. pesreq->dwpidHung == 0 ||
  760. pesreq->dwtidHung == 0));
  761. if (FAILED(hr))
  762. {
  763. SetLastError(ERROR_INVALID_PARAMETER);
  764. goto done;
  765. }
  766. // check and make sure that there is a NULL terminator between the
  767. // start of the string & the end of the buffer
  768. pwszEnd = (LPWSTR)(pBuf + *pcbBuf);
  769. wszEventName = (LPWSTR)(pBuf + (DWORD)pesreq->wszEventName);
  770. for (pwch = wszEventName; pwch < pwszEnd && *pwch != L'\0'; pwch++);
  771. if (pwch >= pwszEnd)
  772. {
  773. SetLastError(ERROR_INVALID_PARAMETER);
  774. DBG_MSG("Bad event name");
  775. goto done;
  776. }
  777. // Get the handle to remote process
  778. hprocRemote = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION,
  779. FALSE, pesreq->pidReqProcess);
  780. if (hprocRemote == NULL)
  781. {
  782. DBG_MSG("Can't get handle to process");
  783. goto done;
  784. }
  785. ZeroMemory(&osvi, sizeof(osvi));
  786. osvi.dwOSVersionInfoSize = sizeof(osvi);
  787. GetVersionExW((LPOSVERSIONINFOW)&osvi);
  788. if ((osvi.wSuiteMask & (VER_SUITE_TERMINAL | VER_SUITE_SINGLEUSERTS)) != 0)
  789. {
  790. WINSTATIONINFORMATIONW wsi;
  791. DWORD cb;
  792. ZeroMemory(&wsi, sizeof(wsi));
  793. fRet = WinStationQueryInformationW(SERVERNAME_CURRENT,
  794. pesreq->ulSessionId,
  795. WinStationInformation,
  796. &wsi, sizeof(wsi), &cb);
  797. if (fRet == FALSE)
  798. {
  799. DBG_MSG("WinStationQI failed");
  800. goto doneTSTokenFetch;
  801. }
  802. // if the session where the hang was terminated isn't active (which
  803. // would be an odd state to be in- perhaps it is closing down?)
  804. // then just bail cuz we don't want to put up UI in it
  805. if (wsi.ConnectState != State_Active)
  806. {
  807. DBG_MSG("No Active Session found!");
  808. SetLastError(0);
  809. goto done;
  810. }
  811. // fetch the token associated with the sessions user
  812. wsut.ProcessId = LongToHandle(GetCurrentProcessId());
  813. wsut.ThreadId = LongToHandle(GetCurrentThreadId());
  814. fRet = WinStationQueryInformationW(SERVERNAME_CURRENT,
  815. pesreq->ulSessionId,
  816. WinStationUserToken, &wsut,
  817. sizeof(wsut), &cbWrote);
  818. if (fRet)
  819. {
  820. if (wsut.UserToken != NULL)
  821. hTokenUser = wsut.UserToken;
  822. else
  823. {
  824. DBG_MSG("no token found");
  825. fRet = FALSE;
  826. }
  827. }
  828. }
  829. else
  830. {
  831. DBG_MSG("WTS not found");
  832. fRet = FALSE;
  833. }
  834. doneTSTokenFetch:
  835. if (fRet == FALSE)
  836. {
  837. DWORD dwERSvcSession = (DWORD)-1;
  838. // make sure the hung app is in our session before using this API and
  839. // if it's not, just bail.
  840. fRet = ProcessIdToSessionId(GetCurrentProcessId(), &dwERSvcSession);
  841. if (fRet == FALSE)
  842. {
  843. DBG_MSG("Failed in ProcessIdToSessionId");
  844. goto done;
  845. }
  846. if (dwERSvcSession != pesreq->ulSessionId)
  847. {
  848. DBG_MSG("Session IDs do not match");
  849. goto done;
  850. }
  851. fRet = GetInteractiveUsersToken(&hTokenUser);
  852. if (fRet == FALSE)
  853. {
  854. DBG_MSG("Failure in GetInteractiveUsersToken");
  855. goto done;
  856. }
  857. }
  858. // create the default environment for the user token- note that we
  859. // have to set the CREATE_UNICODE_ENVIRONMENT flag...
  860. fRet = CreateEnvironmentBlock(&pvEnv, hTokenUser, FALSE);
  861. if (fRet == FALSE)
  862. pvEnv = NULL;
  863. // note that we do not allow inheritance of handles cuz they would be
  864. // inherited from this process and not the real parent, making it sorta
  865. // pointless.
  866. ZeroMemory(&si, sizeof(si));
  867. si.cb = sizeof(si);
  868. #ifdef _WIN64
  869. if (pesreq->fIs64bit == FALSE)
  870. cch = GetSystemWow64DirectoryW(wszSysDir, sizeofSTRW(wszSysDir));
  871. else
  872. #endif
  873. cch = GetSystemDirectoryW(wszSysDir, sizeofSTRW(wszSysDir));
  874. if (cch == 0)
  875. goto done;
  876. cchNeed = cch + sizeofSTRW(c_wszQSubdir) + 2;
  877. if (cchNeed > sizeofSTRW(wszSysDir))
  878. {
  879. __try { pwszSysDir = (WCHAR *)_alloca(cchNeed * sizeof(WCHAR)); }
  880. __except(EXCEPTION_STACK_OVERFLOW) { pwszSysDir = NULL; }
  881. if (pwszSysDir == NULL)
  882. {
  883. SetLastError(ERROR_OUTOFMEMORY);
  884. goto done;
  885. }
  886. if (cch > sizeofSTRW(wszSysDir))
  887. {
  888. #ifdef _WIN64
  889. if (pesreq->fIs64bit == FALSE)
  890. cch = GetSystemWow64DirectoryW(pwszSysDir, cchNeed);
  891. else
  892. #endif
  893. cch = GetSystemDirectoryW(pwszSysDir, cchNeed);
  894. }
  895. else
  896. {
  897. wcscpy(pwszSysDir, wszSysDir);
  898. }
  899. }
  900. else
  901. {
  902. pwszSysDir = wszSysDir;
  903. }
  904. // if the length of the system directory is 3, then %windir% is in the form
  905. // of X:\, so clip off the backslash so we don't have to special case
  906. // it below...
  907. if (cch == 3)
  908. *(pwszSysDir + 2) = L'\0';
  909. // compute the size of the buffer to hold the command line. 14 is for
  910. // the max # of characters in a DWORD
  911. cchNeed = 12 + cch + wcslen((LPWSTR)wszEventName) +
  912. sizeofSTRW(c_wszDWMCmdLine64);
  913. __try { pwszCmdLine = (WCHAR *)_alloca(cchNeed * sizeof(WCHAR)); }
  914. __except(EXCEPTION_STACK_OVERFLOW) { pwszCmdLine = NULL; }
  915. if (pwszCmdLine == NULL)
  916. {
  917. SetLastError(ERROR_OUTOFMEMORY);
  918. goto done;
  919. }
  920. #ifdef _WIN64
  921. swprintf(pwszCmdLine, c_wszDWMCmdLine64, pwszSysDir, pesreq->dwpidHung,
  922. ((pesreq->fIs64bit) ? L'6' : L' '), pesreq->dwtidHung,
  923. wszEventName);
  924. #else
  925. swprintf(pwszCmdLine, c_wszDWMCmdLine32, pwszSysDir, pesreq->dwpidHung,
  926. pesreq->dwtidHung, wszEventName);
  927. #endif
  928. TESTBOOL(hr, CreateProcessAsUserW(hTokenUser, NULL, pwszCmdLine, NULL, NULL,
  929. FALSE, CREATE_DEFAULT_ERROR_MODE |
  930. CREATE_UNICODE_ENVIRONMENT |
  931. NORMAL_PRIORITY_CLASS, pvEnv, pwszSysDir,
  932. &si, &pi));
  933. if (FAILED(hr))
  934. {
  935. DBG_MSG("CreateProcessAsUser failed");
  936. fRet = FALSE;
  937. goto done;
  938. }
  939. // duplicate the process & thread handles back into the remote process
  940. fRet = DuplicateHandle(GetCurrentProcess(), pi.hProcess, hprocRemote,
  941. &esrep.hProcess, 0, FALSE, DUPLICATE_SAME_ACCESS);
  942. if (fRet == FALSE)
  943. esrep.hProcess = NULL;
  944. // get rid of any errors we might have encountered
  945. SetLastError(0);
  946. fRet = TRUE;
  947. esrep.ess = essOk;
  948. done:
  949. esrep.dwErr = GetLastError();
  950. // build the reply packet with the handle valid in the context
  951. // of the requesting process
  952. RtlCopyMemory(pBuf, &esrep, sizeof(esrep));
  953. *pcbBuf = sizeof(esrep);
  954. // close our versions of the handles. The requestors references
  955. // are now the main ones
  956. if (hTokenUser != NULL)
  957. CloseHandle(hTokenUser);
  958. if (pvEnv != NULL)
  959. DestroyEnvironmentBlock(pvEnv);
  960. if (pi.hProcess != NULL)
  961. CloseHandle(pi.hProcess);
  962. if (pi.hThread != NULL)
  963. CloseHandle(pi.hThread);
  964. if (hprocRemote != NULL)
  965. CloseHandle(hprocRemote);
  966. return fRet;
  967. }