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.

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