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.

2367 lines
69 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. validate.c
  5. Abstract:
  6. Implementation of file validation.
  7. Author:
  8. Wesley Witt (wesw) 18-Dec-1998
  9. Revision History:
  10. Andrew Ritz (andrewr) 7-Jul-1999 : added comments
  11. --*/
  12. #include "sfcp.h"
  13. #pragma hdrstop
  14. #include <winwlx.h>
  15. //
  16. // handle to validation thread
  17. //
  18. HANDLE hErrorThread;
  19. //
  20. // list of files that need to be checked in the validation request queue
  21. //
  22. LIST_ENTRY SfcErrorQueue;
  23. //
  24. // count of files in queue
  25. //
  26. ULONG ErrorQueueCount;
  27. //
  28. // event that's signalled when a new event is placed into the queue
  29. //
  30. HANDLE ErrorQueueEvent;
  31. //
  32. // critical section used to synchronize insertion and deletion from the file
  33. // restore list
  34. //
  35. RTL_CRITICAL_SECTION ErrorCs;
  36. //
  37. // this records how much space in our dllcache has been consumed. CacheUsed
  38. // should never be larger than the quota.
  39. //
  40. ULONGLONG CacheUsed;
  41. //
  42. // records the currently logged on user's name (for logging)
  43. //
  44. WCHAR LoggedOnUserName[MAX_PATH];
  45. //
  46. // set to TRUE if a user is logged onto the system
  47. //
  48. BOOL UserLoggedOn;
  49. //
  50. // set to TRUE if we're in the middle of a scan
  51. //
  52. BOOL ScanInProgress;
  53. //
  54. // event that is signalled to cancel the scanning of the system
  55. //
  56. HANDLE hEventScanCancel;
  57. //
  58. // event that is signalled when the cancel has been completed
  59. //
  60. HANDLE hEventScanCancelComplete;
  61. //
  62. // used to handle files that need to come from media which do not
  63. // require UI to restore
  64. //
  65. RESTORE_QUEUE SilentRestoreQueue;
  66. //
  67. // used to handle files that need to come from media which require UI to
  68. // restore
  69. //
  70. RESTORE_QUEUE UIRestoreQueue;
  71. //
  72. // handles to user's desktop and token
  73. //
  74. HDESK hUserDesktop;
  75. HANDLE hUserToken;
  76. //
  77. // indicates if WFP can receive anymore validation requests or not.
  78. //
  79. BOOL ShuttingDown = FALSE;
  80. //
  81. // This event is signalled when WFP is idle and no longer processing any
  82. // validation requests. An external process can synchronize on this process
  83. // so that it knows WFP is idle before shutting down the system
  84. //
  85. HANDLE hEventIdle;
  86. //
  87. // prototypes
  88. //
  89. BOOL
  90. pSfcHandleAllOrphannedRequests(
  91. VOID
  92. );
  93. BOOL
  94. SfcValidateFileSignature(
  95. IN HCATADMIN hCatAdmin,
  96. IN HANDLE RealFileHandle,
  97. IN PCWSTR BaseFileName,
  98. IN PCWSTR CompleteFileName
  99. )
  100. /*++
  101. Routine Description:
  102. Checks if the signature for a given file is valid using WinVerifyTrust
  103. Arguments:
  104. hCatAdmin - admin context handle for checking file signature
  105. RealFileHandle - file handle to the file to be verified
  106. BaseFileName - filename without the path of the file to be verified
  107. CompleteFileName - fully qualified filename with path
  108. Return Value:
  109. TRUE if the file has a valid signature.
  110. --*/
  111. {
  112. BOOL rVal = FALSE;
  113. DWORD HashSize;
  114. LPBYTE Hash = NULL;
  115. ULONG SigErr = ERROR_SUCCESS;
  116. WINTRUST_DATA WintrustData;
  117. WINTRUST_CATALOG_INFO WintrustCatalogInfo;
  118. WINTRUST_FILE_INFO WintrustFileInfo;
  119. WCHAR UnicodeKey[MAX_PATH];
  120. HCATINFO PrevCat;
  121. HCATINFO hCatInfo;
  122. CATALOG_INFO CatInfo;
  123. DRIVER_VER_INFO OsAttrVersionInfo;
  124. OSVERSIONINFO OsVersionInfo;
  125. //
  126. // initialize some of the structure that we will pass into winverifytrust.
  127. // we don't know if we're checking against a catalog or directly against a
  128. // file at this point
  129. //
  130. ZeroMemory(&WintrustData, sizeof(WINTRUST_DATA));
  131. WintrustData.cbStruct = sizeof(WINTRUST_DATA);
  132. WintrustData.dwUIChoice = WTD_UI_NONE;
  133. WintrustData.fdwRevocationChecks = WTD_REVOKE_NONE;
  134. WintrustData.dwStateAction = WTD_STATEACTION_AUTO_CACHE;
  135. WintrustData.pCatalog = &WintrustCatalogInfo;
  136. WintrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
  137. Hash = NULL;
  138. //
  139. // Initialize the DRIVER_VER_INFO structure to validate
  140. // against 5.0 and 5.1 OSATTR
  141. //
  142. ZeroMemory( &OsVersionInfo, sizeof(OSVERSIONINFO));
  143. OsVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  144. ZeroMemory(&OsAttrVersionInfo, sizeof(DRIVER_VER_INFO));
  145. OsAttrVersionInfo.cbStruct = sizeof(DRIVER_VER_INFO);
  146. OsAttrVersionInfo.dwPlatform = VER_PLATFORM_WIN32_NT;
  147. OsAttrVersionInfo.sOSVersionLow.dwMajor = 5;
  148. OsAttrVersionInfo.sOSVersionLow.dwMinor = 0;
  149. if (GetVersionEx(&OsVersionInfo)) {
  150. OsAttrVersionInfo.sOSVersionHigh.dwMajor = OsVersionInfo.dwMajorVersion;
  151. OsAttrVersionInfo.sOSVersionHigh.dwMinor = OsVersionInfo.dwMinorVersion;
  152. //Set this only if all went well
  153. WintrustData.pPolicyCallbackData = (LPVOID)(&OsAttrVersionInfo);
  154. }else{
  155. DebugPrint1( LVL_MINIMAL, L"Could not get OS Version while validating file - GetVersionEx failed (%d)", GetLastError() );
  156. }
  157. //
  158. // we first calculate a hash for our file. start with a reasonable
  159. // hash size and grow larger as needed
  160. //
  161. HashSize = 100;
  162. do {
  163. Hash = MemAlloc( HashSize );
  164. if(!Hash) {
  165. DebugPrint( LVL_MINIMAL, L"Not enough memory to verify file signature" );
  166. SigErr = ERROR_NOT_ENOUGH_MEMORY;
  167. break;
  168. }
  169. if(CryptCATAdminCalcHashFromFileHandle(RealFileHandle,
  170. &HashSize,
  171. Hash,
  172. 0)) {
  173. SigErr = ERROR_SUCCESS;
  174. } else {
  175. SigErr = GetLastError();
  176. ASSERT(SigErr != ERROR_SUCCESS);
  177. //
  178. // If this API did mess up and not set last error, go ahead
  179. // and set something.
  180. //
  181. if(SigErr == ERROR_SUCCESS) {
  182. SigErr = ERROR_INVALID_DATA;
  183. }
  184. MemFree( Hash );
  185. Hash = NULL; // reset this so we won't try to free it later
  186. if(SigErr != ERROR_INSUFFICIENT_BUFFER) {
  187. //
  188. // The API failed for some reason other than
  189. // buffer-too-small. We gotta bail.
  190. //
  191. DebugPrint1( LVL_MINIMAL,
  192. L"CCACHFFH() failed, ec=0x%08x",
  193. SigErr );
  194. break;
  195. }
  196. }
  197. } while (SigErr != ERROR_SUCCESS);
  198. if (SigErr != ERROR_SUCCESS) {
  199. //
  200. // if we failed at this point there are a few reasons:
  201. //
  202. //
  203. // 1) a bug in this code
  204. // 2) we are in a low memory situation
  205. // 3) the file's hash cannot be calculated on purpose (in the case
  206. // of a catalog file, a hash cannot be calculated because a catalog
  207. // cannot sign another catalog. In this case, we check to see if
  208. // the file is "self-signed".
  209. hCatInfo = NULL;
  210. goto selfsign;
  211. }
  212. //
  213. // Now we have the file's hash. Initialize the structures that
  214. // will be used later on in calls to WinVerifyTrust.
  215. //
  216. WintrustData.dwUnionChoice = WTD_CHOICE_CATALOG;
  217. ZeroMemory(&WintrustCatalogInfo, sizeof(WINTRUST_CATALOG_INFO));
  218. WintrustCatalogInfo.cbStruct = sizeof(WINTRUST_CATALOG_INFO);
  219. WintrustCatalogInfo.pbCalculatedFileHash = Hash;
  220. WintrustCatalogInfo.cbCalculatedFileHash = HashSize;
  221. //
  222. // WinVerifyTrust is case-sensitive, so ensure that the key
  223. // being used is all lower-case!
  224. //
  225. // Copy the key to a writable Unicode character buffer so we
  226. // can lower-case it.
  227. //
  228. wcsncpy(UnicodeKey, BaseFileName, UnicodeChars(UnicodeKey));
  229. // in theory, we don't know what the size of BaseFileName is...
  230. UnicodeKey[UnicodeChars(UnicodeKey) - 1] = '\0';
  231. MyLowerString(UnicodeKey, wcslen(UnicodeKey));
  232. WintrustCatalogInfo.pcwszMemberTag = UnicodeKey;
  233. //
  234. // Search through installed catalogs looking for those that
  235. // contain data for a file with the hash we just calculated.
  236. //
  237. PrevCat = NULL;
  238. hCatInfo = CryptCATAdminEnumCatalogFromHash(
  239. hCatAdmin,
  240. Hash,
  241. HashSize,
  242. 0,
  243. &PrevCat
  244. );
  245. if (hCatInfo == NULL) {
  246. SigErr = GetLastError();
  247. DebugPrint2( LVL_MINIMAL,
  248. L"CCAECFH() failed for (%ws), ec=%d",
  249. UnicodeKey,
  250. SigErr );
  251. }
  252. while(hCatInfo) {
  253. CatInfo.cbStruct = sizeof(CATALOG_INFO);
  254. if (CryptCATCatalogInfoFromContext(hCatInfo, &CatInfo, 0)) {
  255. //
  256. // Attempt to validate against each catalog we
  257. // enumerate. Note that the catalog file info we
  258. // get back gives us a fully qualified path.
  259. //
  260. // NOTE: Because we're using cached
  261. // catalog information (i.e., the
  262. // WTD_STATEACTION_AUTO_CACHE flag), we
  263. // don't need to explicitly validate the
  264. // catalog itself first.
  265. //
  266. WintrustCatalogInfo.pcwszCatalogFilePath = CatInfo.wszCatalogFile;
  267. SigErr = (DWORD)WinVerifyTrust(
  268. NULL,
  269. &DriverVerifyGuid,
  270. &WintrustData
  271. );
  272. //
  273. // If the result of the above validations is
  274. // success, then we're done.
  275. //
  276. if(SigErr == ERROR_SUCCESS) {
  277. //
  278. // note: this API has odd semantics.
  279. // in the success case, we must release the catalog info handle
  280. // in the failure case, we implicitly free PrevCat
  281. // if we explicitly free the catalog, we will double free the
  282. // handle!!!
  283. //
  284. CryptCATAdminReleaseCatalogContext(hCatAdmin,hCatInfo,0);
  285. break;
  286. } else {
  287. DebugPrint1( LVL_MINIMAL, L"WinVerifyTrust(1) failed, ec=0x%08x", SigErr );
  288. }
  289. //
  290. // Free the pcSignerCertContext member of the DRIVER_VER_INFO struct
  291. // that was allocated in our call to WinVerifyTrust.
  292. //
  293. if (OsAttrVersionInfo.pcSignerCertContext != NULL) {
  294. CertFreeCertificateContext(OsAttrVersionInfo.pcSignerCertContext);
  295. OsAttrVersionInfo.pcSignerCertContext = NULL;
  296. }
  297. }
  298. PrevCat = hCatInfo;
  299. hCatInfo = CryptCATAdminEnumCatalogFromHash(hCatAdmin, Hash, HashSize, 0, &PrevCat);
  300. }
  301. selfsign:
  302. if (hCatInfo == NULL) {
  303. //
  304. // We exhausted all the applicable catalogs without
  305. // finding the one we needed.
  306. //
  307. SigErr = GetLastError();
  308. ASSERT(SigErr != ERROR_SUCCESS);
  309. //
  310. // Make sure we have a valid error code.
  311. //
  312. if(SigErr == ERROR_SUCCESS) {
  313. SigErr = ERROR_INVALID_DATA;
  314. }
  315. //
  316. // The file failed to validate using the specified
  317. // catalog. See if the file validates without a
  318. // catalog (i.e., the file contains its own
  319. // signature).
  320. //
  321. WintrustData.dwUnionChoice = WTD_CHOICE_FILE;
  322. WintrustData.pFile = &WintrustFileInfo;
  323. ZeroMemory(&WintrustFileInfo, sizeof(WINTRUST_FILE_INFO));
  324. WintrustFileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO);
  325. WintrustFileInfo.pcwszFilePath = CompleteFileName;
  326. WintrustFileInfo.hFile = RealFileHandle;
  327. SigErr = (DWORD)WinVerifyTrust(
  328. NULL,
  329. &DriverVerifyGuid,
  330. &WintrustData
  331. );
  332. if(SigErr != ERROR_SUCCESS) {
  333. DebugPrint2( LVL_MINIMAL, L"WinVerifyTrust(2) failed [%ws], ec=0x%08x", WintrustData.pFile->pcwszFilePath,SigErr );
  334. //
  335. // in this case the file is not in any of our catalogs
  336. // and it does not contain it's own signature
  337. //
  338. }
  339. //
  340. // Free the pcSignerCertContext member of the DRIVER_VER_INFO struct
  341. // that was allocated in our call to WinVerifyTrust.
  342. //
  343. if (OsAttrVersionInfo.pcSignerCertContext != NULL) {
  344. CertFreeCertificateContext(OsAttrVersionInfo.pcSignerCertContext);
  345. OsAttrVersionInfo.pcSignerCertContext = NULL;
  346. }
  347. }
  348. if(SigErr == ERROR_SUCCESS) {
  349. rVal = TRUE;
  350. }
  351. if (Hash) {
  352. MemFree( Hash );
  353. }
  354. return rVal;
  355. }
  356. DWORD
  357. GetPageFileSize(
  358. VOID
  359. )
  360. /*++
  361. Routine Description:
  362. This function will only be needed if we're being called from Setup.
  363. The problem is that Setup has decided how much of a pagefile will
  364. be needed on the next reboot, but doesn't actually generate a pagefile,
  365. therefore, the disk space appears to be free.
  366. This function will go look in the registry, and determine the
  367. size of the pagefile (that isn't really on disk).
  368. Note that we only care about the pagefile if it's going to be
  369. on the partition where the file cache is installed.
  370. Arguments:
  371. NONE.
  372. Return Value:
  373. If successful, returns the size of the pagefile. Otherwise, we're
  374. going to return 0.
  375. --*/
  376. {
  377. #if 0
  378. DWORD SetupMode = 0;
  379. #endif
  380. PWSTR PageFileString = 0;
  381. PWSTR SizeString;
  382. #if 0
  383. WCHAR WindowsDirectory[MAX_PATH];
  384. #endif
  385. DWORD PageFileSize = 0;
  386. //
  387. // Determine if we're in Setup mode.
  388. //
  389. #if 0
  390. SetupMode = SfcQueryRegDword(
  391. L"\\Registry\\Machine\\System\\Setup",
  392. L"SystemSetupInProgress",
  393. 0
  394. );
  395. if( SetupMode == 0 ) {
  396. return( 0 );
  397. }
  398. #else
  399. if (SFCDisable != SFC_DISABLE_SETUP) {
  400. return( 0 );
  401. }
  402. #endif
  403. //
  404. // Go get the pagefile string out of the registry.
  405. //
  406. // Note that the pagefile string is really a REG_MULTI_SZ,
  407. // but we're only going to pay attention to the first string
  408. // returned.
  409. //
  410. //
  411. PageFileString = SfcQueryRegString(
  412. L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager\\Memory Management",
  413. L"PagingFiles"
  414. );
  415. if( PageFileString == NULL ) {
  416. return( 0 );
  417. }
  418. //
  419. // Is the pagefile even on the cache drive?
  420. //
  421. // Note that the user can have multiple pagefiles. We're going to
  422. // look at the first one. Any other pagefiles, and the user is on his own.
  423. //
  424. #if 0
  425. GetWindowsDirectory( WindowsDirectory, MAX_PATH );
  426. if( WindowsDirectory[0] != PageFileString[0] ) {
  427. #else
  428. if( towlower(SfcProtectedDllPath.Buffer[0]) != towlower(PageFileString[0]) ) {
  429. #endif
  430. MemFree( PageFileString );
  431. return( 0 );
  432. }
  433. //
  434. // How big is the pagefile?
  435. //
  436. SizeString = wcsrchr( PageFileString, L' ' );
  437. if (SizeString != NULL) {
  438. PageFileSize = wcstoul( SizeString + 1, NULL, 10 );
  439. } else {
  440. PageFileSize = 0;
  441. }
  442. //
  443. // Default.
  444. //
  445. MemFree( PageFileString );
  446. return PageFileSize;
  447. }
  448. BOOL
  449. SfcPopulateCache(
  450. IN HWND ProgressWindow,
  451. IN BOOL Validate,
  452. IN BOOL AllowUI,
  453. IN PCWSTR IgnoreFiles OPTIONAL
  454. )
  455. /*++
  456. Routine Description:
  457. This routine is used to populate the dll cache directory.
  458. We add files in order of their insertion in our list (so note that we have
  459. to put files we *really want in the cache* at the head of the list.) We
  460. continue to add files until we run out of space compared to our quota.
  461. Arguments:
  462. ProgressWindow - handle to progress control that is stepped as we add
  463. each file to the cache
  464. Validate - if TRUE, we should make sure that each file we're adding
  465. is valid before moving the file into the cache
  466. AllowUI - if FALSE, do not emit any UI
  467. Return Value:
  468. If successful, returns TRUE.
  469. --*/
  470. {
  471. ULONG i;
  472. PSFC_REGISTRY_VALUE RegVal;
  473. VALIDATION_REQUEST_DATA vrd;
  474. NTSTATUS Status;
  475. HANDLE hFile;
  476. ULARGE_INTEGER FreeBytesAvailableToCaller;
  477. ULARGE_INTEGER TotalNumberOfBytes;
  478. ULARGE_INTEGER TotalNumberOfFreeBytes;
  479. ULONGLONG FileSize;
  480. IO_STATUS_BLOCK IoStatusBlock;
  481. FILE_STANDARD_INFORMATION StandardInfo;
  482. HANDLE DirHandle;
  483. WCHAR Drive[8];
  484. HCATADMIN hCatAdmin = NULL;
  485. ULONGLONG RequiredFreeSpace;
  486. BOOL Cancelled = FALSE;
  487. DWORD LastErrorInvalidFile = ERROR_SUCCESS;
  488. DWORD LastErrorCache = ERROR_SUCCESS;
  489. UNICODE_STRING tmpString;
  490. PCWSTR FileNameOnMedia;
  491. BOOL DoCopy;
  492. WCHAR InfFileName[MAX_PATH];
  493. BOOL ExcepPackFile;
  494. //
  495. // if we're in the middle of scanning, we shouldn't touch the cache since
  496. // the two functions will step on each other.
  497. //
  498. if (ScanInProgress) {
  499. return TRUE;
  500. }
  501. DebugPrint( LVL_MINIMAL, L"SfcPopulateCache entry..." );
  502. //
  503. // start the scan and log the message, but don't log anything if we're
  504. // inside gui-mode setup
  505. //
  506. ScanInProgress = TRUE;
  507. if (SFCDisable != SFC_DISABLE_SETUP) {
  508. SfcReportEvent( MSG_SCAN_STARTED, NULL, NULL, 0 );
  509. }
  510. //
  511. // How big does our freespace buffer need to be?
  512. //
  513. RequiredFreeSpace = (GetPageFileSize() + SFC_REQUIRED_FREE_SPACE)* ONE_MEG;
  514. DebugPrint2( LVL_MINIMAL, L"RequiredFreeSpace = %d, SFCQuota = %I64d", RequiredFreeSpace, SFCQuota );
  515. //
  516. // try to initialize crypto here as this could be the first use of it (from SfcInitProt or SfcInitiateScan)
  517. //
  518. Status = LoadCrypto();
  519. if(!NT_SUCCESS(Status))
  520. return FALSE;
  521. if(!CryptCATAdminAcquireContext(&hCatAdmin, &DriverVerifyGuid, 0)) {
  522. DebugPrint1( LVL_MINIMAL, L"CCAAC() failed, ec=%d", GetLastError() );
  523. return FALSE;
  524. }
  525. //
  526. // Flush the Cache once before we start any Crypto operations
  527. //
  528. SfcFlushCryptoCache();
  529. //
  530. // Refresh exception packages info
  531. //
  532. SfcRefreshExceptionInfo();
  533. CacheUsed = 0;
  534. Drive[2] = L'\\';
  535. Drive[3] = 0;
  536. //
  537. // iterate through the list of files we're protecting
  538. //
  539. // 1. make sure the entry is ok
  540. // 2. if we're supposed to, check the signature of the file and restore if
  541. // necessary.
  542. // 3. if there is space available, copy the file into the cache from:
  543. // a) if the file is present on disk, use it
  544. // b) if the file isn't present from appropriate media
  545. // 4. add the size of the file to the total cache size and make sure the
  546. // file we put in the cache is properly signed
  547. for (i=0; i<SfcProtectedDllCount; i++) {
  548. RegVal = &SfcProtectedDllsList[i];
  549. DebugPrint2( LVL_VERBOSE, L"Processing protected file [%ws] [%ws]", RegVal->FullPathName.Buffer, RegVal->SourceFileName.Buffer );
  550. //
  551. // We step the guage once per file
  552. //
  553. if (ProgressWindow != NULL) {
  554. PostMessage( ProgressWindow, PBM_STEPIT, 0, 0 );
  555. }
  556. if (RegVal->DirName.Buffer[0] == 0 || RegVal->DirName.Buffer[0] == L'\\') {
  557. ASSERT(FALSE);
  558. continue;
  559. }
  560. if (NULL == RegVal->DirHandle) {
  561. DebugPrint1(LVL_MINIMAL, L"The dir handle for [%ws] is NULL; skipping the file", RegVal->DirName.Buffer);
  562. continue;
  563. }
  564. //
  565. // check if the user clicked cancel, and if so, exit
  566. //
  567. if (hEventScanCancel) {
  568. if (WaitForSingleObject( hEventScanCancel, 0 ) == WAIT_OBJECT_0) {
  569. Cancelled = TRUE;
  570. break;
  571. }
  572. }
  573. #if DBG
  574. //
  575. // don't protect the SFC files in the debug build
  576. //
  577. if (_wcsnicmp( RegVal->FileName.Buffer, L"sfc", 3 ) == 0) {
  578. continue;
  579. }
  580. #endif
  581. //
  582. // get the inf name here
  583. //
  584. ExcepPackFile = SfcGetInfName(RegVal, InfFileName);
  585. if (Validate) {
  586. //
  587. // also make sure the file is valid... if we're putting a file
  588. // in the cache, then don't log anything (SyncOnly = TRUE)
  589. //
  590. RtlZeroMemory( &vrd, sizeof(vrd) );
  591. vrd.RegVal = RegVal;
  592. vrd.SyncOnly = TRUE;
  593. vrd.ImageValData.EventLog = MSG_SCAN_FOUND_BAD_FILE;
  594. //
  595. // set the validation data
  596. //
  597. SfcGetValidationData( &RegVal->FileName,
  598. &RegVal->FullPathName,
  599. RegVal->DirHandle,
  600. hCatAdmin,
  601. &vrd.ImageValData.New);
  602. //
  603. // check the signature
  604. //
  605. SfcValidateDLL( &vrd, hCatAdmin );
  606. //
  607. // If the source file is present and is unsigned, we must restore
  608. // it. If the source file is not present, then we just ignore it
  609. //
  610. if (!vrd.ImageValData.Original.SignatureValid &&
  611. vrd.ImageValData.Original.FilePresent) {
  612. //
  613. // this might be an unsigned F6 driver that should be left alone when running in GUI setup
  614. //
  615. if(SFC_DISABLE_SETUP == SFCDisable && IgnoreFiles != NULL)
  616. {
  617. PCWSTR szFile = IgnoreFiles;
  618. USHORT usLen;
  619. for(;;)
  620. {
  621. usLen = (USHORT) wcslen(szFile);
  622. if(0 == usLen || (usLen * sizeof(WCHAR) == RegVal->FullPathName.Length &&
  623. 0 == _wcsnicmp(szFile, RegVal->FullPathName.Buffer, usLen)))
  624. {
  625. break;
  626. }
  627. szFile += usLen + 1;
  628. }
  629. if(usLen != 0)
  630. {
  631. continue;
  632. }
  633. }
  634. //
  635. // see if we can restore from cache
  636. //
  637. if (!vrd.ImageValData.RestoreFromMedia) {
  638. SfcRestoreFromCache( &vrd, hCatAdmin );
  639. }
  640. if (vrd.ImageValData.RestoreFromMedia) {
  641. //
  642. // the file is still bad so we need to restore from the
  643. // media
  644. //
  645. if (!SfcRestoreFileFromInstallMedia(
  646. &vrd,
  647. RegVal->FileName.Buffer,
  648. RegVal->FileName.Buffer,
  649. RegVal->DirName.Buffer,
  650. RegVal->SourceFileName.Buffer,
  651. InfFileName,
  652. ExcepPackFile,
  653. FALSE, // target is NOT cache
  654. AllowUI,
  655. NULL )) {
  656. LastErrorInvalidFile = GetLastError();
  657. SfcReportEvent(
  658. ((LastErrorInvalidFile != ERROR_CANCELLED)
  659. ? MSG_RESTORE_FAILURE
  660. : (SFCNoPopUps == TRUE)
  661. ? MSG_COPY_CANCEL_NOUI
  662. : MSG_COPY_CANCEL ),
  663. RegVal->FullPathName.Buffer,
  664. &vrd.ImageValData,
  665. LastErrorInvalidFile);
  666. DebugPrint1(
  667. LVL_MINIMAL,
  668. L"Failed to restore file from install media [%ws]",
  669. RegVal->FileName.Buffer );
  670. } else {
  671. DebugPrint1(
  672. LVL_VERBOSE,
  673. L"The file was successfully restored from install media [%ws]",
  674. RegVal->FileName.Buffer );
  675. SfcReportEvent(
  676. MSG_SCAN_FOUND_BAD_FILE,
  677. RegVal->FullPathName.Buffer,
  678. &vrd.ImageValData,
  679. ERROR_SUCCESS );
  680. }
  681. } else {
  682. ASSERT(vrd.ImageValData.New.SignatureValid == TRUE);
  683. }
  684. }
  685. }
  686. //
  687. // see how much space we have left
  688. //
  689. Drive[0] = SfcProtectedDllPath.Buffer[0];
  690. Drive[1] = SfcProtectedDllPath.Buffer[1];
  691. if (!GetDiskFreeSpaceEx( Drive, &FreeBytesAvailableToCaller, &TotalNumberOfBytes, &TotalNumberOfFreeBytes ) ||
  692. TotalNumberOfFreeBytes.QuadPart <= RequiredFreeSpace)
  693. {
  694. DebugPrint( LVL_MINIMAL, L"Not enough free space" );
  695. //
  696. // if we're validating, we want to keep going through the list even
  697. // though we're out of space
  698. //
  699. if (Validate) {
  700. continue;
  701. } else {
  702. break;
  703. }
  704. }
  705. if (CacheUsed >= SFCQuota) {
  706. DebugPrint( LVL_MINIMAL, L"Cache is full" );
  707. //
  708. // if we're validating, we want to keep going through the list even
  709. // though we're out of space
  710. //
  711. if (Validate) {
  712. continue;
  713. } else {
  714. break;
  715. }
  716. }
  717. ASSERT(RegVal->DirHandle != NULL);
  718. DirHandle = RegVal->DirHandle;
  719. if (!DirHandle) {
  720. DebugPrint1( LVL_MINIMAL, L"invalid dirhandle for dir [%ws]", RegVal->DirName.Buffer );
  721. continue;
  722. }
  723. //
  724. // Either copy the file or restore from media...
  725. //
  726. // Let's make an optimization here that says that if the file is in
  727. // the driver cache, we don't have to spend any time putting the
  728. // file in the dllcache since we will probably be able to get at
  729. // the file later on. This will also save disk space during the
  730. // initial scan
  731. //
  732. DoCopy = TRUE;
  733. if (SFCDisable == SFC_DISABLE_SETUP) {
  734. PCWSTR TempCabName;
  735. TempCabName = IsFileInDriverCache( SpecialFileNameOnMedia( RegVal ));
  736. if (TempCabName) {
  737. MemFree((PVOID)TempCabName);
  738. DoCopy = FALSE;
  739. }
  740. }
  741. if (DoCopy) {
  742. PCWSTR OnDiskFileName;
  743. OnDiskFileName = FileNameOnMedia( RegVal );
  744. FileNameOnMedia = SpecialFileNameOnMedia( RegVal );
  745. Status = STATUS_UNSUCCESSFUL;
  746. //
  747. // see if the file is cached. the filename in the cache will
  748. // have the same name as the filename on the media. Note that
  749. // we don't use the "FileNameOnMedia" routine to get
  750. // this information because of special case files like the NT
  751. // kernel and HALs. In these special case files, we should
  752. // only copy the current file on disk to the cache if it
  753. // corresponds to the source filename.
  754. //
  755. if (_wcsicmp( OnDiskFileName, FileNameOnMedia) == 0) {
  756. Status = SfcOpenFile( &RegVal->FileName, DirHandle, SHARE_ALL, &hFile );
  757. }
  758. if (NT_SUCCESS(Status) ) {
  759. NtClose( hFile );
  760. RtlInitUnicodeString( &tmpString, FileNameOnMedia );
  761. Status = SfcCopyFile(
  762. DirHandle,
  763. RegVal->DirName.Buffer,
  764. SfcProtectedDllFileDirectory,
  765. SfcProtectedDllPath.Buffer,
  766. &tmpString,
  767. &RegVal->FileName
  768. );
  769. if (!NT_SUCCESS(Status)) {
  770. DebugPrint3( LVL_MINIMAL, L"Could not copy file [0x%08x] [%ws] [%ws]", Status, RegVal->FileName.Buffer, RegVal->DirName.Buffer );
  771. }
  772. } else {
  773. if (!SfcRestoreFileFromInstallMedia(
  774. &vrd,
  775. RegVal->FileName.Buffer,
  776. SpecialFileNameOnMedia(RegVal),
  777. SfcProtectedDllPath.Buffer,
  778. RegVal->SourceFileName.Buffer,
  779. InfFileName,
  780. ExcepPackFile,
  781. TRUE, // target is cache
  782. AllowUI,
  783. NULL ))
  784. {
  785. LastErrorCache = GetLastError();
  786. SfcReportEvent( MSG_CACHE_COPY_ERROR, RegVal->FullPathName.Buffer, &vrd.ImageValData, LastErrorCache);
  787. DebugPrint1( LVL_MINIMAL, L"Failed to restore file from install media [%ws]", RegVal->FileName.Buffer );
  788. }
  789. }
  790. //
  791. // get the size of the file we just added to the cache and add it to
  792. // the total cache size. while we have the handle open, it's a good
  793. // time to verify that the file we copied into the cache is indeed
  794. // valid
  795. //
  796. ASSERT(SfcProtectedDllFileDirectory != NULL );
  797. FileNameOnMedia = SpecialFileNameOnMedia(RegVal);
  798. RtlInitUnicodeString( &tmpString, FileNameOnMedia );
  799. Status = SfcOpenFile( &tmpString, SfcProtectedDllFileDirectory, SHARE_ALL, &hFile );
  800. if (NT_SUCCESS(Status) ) {
  801. WCHAR FullPathToCachedFile[MAX_PATH];
  802. wcsncpy(FullPathToCachedFile, SfcProtectedDllPath.Buffer, UnicodeChars(FullPathToCachedFile));
  803. pSetupConcatenatePaths( FullPathToCachedFile, FileNameOnMedia, UnicodeChars(FullPathToCachedFile), NULL);
  804. Status = NtQueryInformationFile(
  805. hFile,
  806. &IoStatusBlock,
  807. &StandardInfo,
  808. sizeof(StandardInfo),
  809. FileStandardInformation
  810. );
  811. if (NT_SUCCESS(Status) ) {
  812. FileSize = StandardInfo.EndOfFile.QuadPart;
  813. DebugPrint2( LVL_MINIMAL, L"file size = [0x%08x] [%ws]", FileSize, RegVal->FileName.Buffer );
  814. } else {
  815. DebugPrint2( LVL_MINIMAL, L"Could not query file information [0x%08x] [%ws]", Status, RegVal->FileName.Buffer );
  816. FileSize = 0;
  817. }
  818. if (!SfcValidateFileSignature(
  819. hCatAdmin,
  820. hFile,
  821. FileNameOnMedia,
  822. FullPathToCachedFile )) {
  823. //
  824. // delete the unsigned file from the cache
  825. //
  826. DebugPrint1( LVL_MINIMAL, L"Cache file has a bad signature [%ws]", RegVal->FileName.Buffer );
  827. SfcDeleteFile( SfcProtectedDllFileDirectory, &tmpString );
  828. FileSize = 0;
  829. }
  830. NtClose( hFile );
  831. } else {
  832. DebugPrint2( LVL_MINIMAL, L"Could not open file [0x%08x] [%ws]", Status, RegVal->FileName.Buffer );
  833. FileSize = 0;
  834. }
  835. DebugPrint4( LVL_MINIMAL,
  836. L"cache size [0x%08x], filesize [0x%08x], new size [0x%08x] (%d)",
  837. CacheUsed, FileSize, CacheUsed+FileSize, (CacheUsed+FileSize)/(1024*1024)
  838. );
  839. CacheUsed += FileSize;
  840. }
  841. }
  842. if (hCatAdmin) {
  843. CryptCATAdminReleaseContext(hCatAdmin,0);
  844. }
  845. //
  846. // log an event saying it succeeded or was cancelled, but only if we're not
  847. // inside gui-setup.
  848. //
  849. if (SFCDisable == SFC_DISABLE_SETUP) {
  850. //
  851. // the user can never cancel inside gui-setup
  852. //
  853. ASSERT(Cancelled == FALSE);
  854. } else {
  855. SfcReportEvent( Cancelled ? MSG_SCAN_CANCELLED : MSG_SCAN_COMPLETED, NULL, NULL, 0 );
  856. }
  857. ScanInProgress = FALSE;
  858. if (hEventScanCancelComplete) {
  859. SetEvent(hEventScanCancelComplete);
  860. }
  861. DebugPrint( LVL_MINIMAL, L"SfcPopulateCache exit..." );
  862. return TRUE;
  863. }
  864. PVALIDATION_REQUEST_DATA
  865. IsFileInQueue(
  866. IN PSFC_REGISTRY_VALUE RegVal
  867. )
  868. /*++
  869. Routine Description:
  870. This routine checks if the specified file is in the validation request
  871. queue.
  872. Note that this routine does no locking of the queue. The caller is
  873. responsible for locking.
  874. Arguments:
  875. RegVal - pointer to structure for file we're concerned with
  876. Return Value:
  877. If the file is already in the queue, returns a pointer to the validation
  878. request corresponding to this item, else NULL.
  879. --*/
  880. {
  881. PVALIDATION_REQUEST_DATA vrd;
  882. PLIST_ENTRY Next;
  883. //
  884. // walk through our linked list of validation requests looking for a match
  885. //
  886. Next = SfcErrorQueue.Flink;
  887. while (Next != &SfcErrorQueue) {
  888. vrd = CONTAINING_RECORD( Next, VALIDATION_REQUEST_DATA, Entry );
  889. Next = vrd->Entry.Flink;
  890. if (RegVal == vrd->RegVal) {
  891. return vrd;
  892. }
  893. }
  894. return NULL;
  895. }
  896. void
  897. RemoveDuplicatesFromQueue(
  898. IN PSFC_REGISTRY_VALUE RegVal
  899. )
  900. /*++
  901. Routine Description:
  902. This routine checks for the specified file is in the validation request
  903. queue and removes any duplicate entries from the queue.
  904. Note that this routine does locking of the queue. The caller is
  905. not responsible for locking.
  906. Arguments:
  907. RegVal - pointer to structure for file we're concerned with
  908. Return Value:
  909. none.
  910. --*/
  911. {
  912. PVALIDATION_REQUEST_DATA vrd;
  913. PLIST_ENTRY Next;
  914. //
  915. // we need to lock down the validation queue since we are modifying the
  916. // list
  917. //
  918. RtlEnterCriticalSection( &ErrorCs );
  919. Next = SfcErrorQueue.Flink;
  920. //
  921. // walk through our linked list of validation requests looking for a match
  922. // and remove any duplicates
  923. while (Next != &SfcErrorQueue) {
  924. vrd = CONTAINING_RECORD( Next, VALIDATION_REQUEST_DATA, Entry );
  925. Next = vrd->Entry.Flink;
  926. if (RegVal == vrd->RegVal) {
  927. RemoveEntryList( &vrd->Entry );
  928. ErrorQueueCount -= 1;
  929. MemFree( vrd );
  930. }
  931. }
  932. RtlLeaveCriticalSection( &ErrorCs );
  933. }
  934. BOOL
  935. IsUserValid(
  936. IN HANDLE Token,
  937. IN DWORD Rid
  938. )
  939. /*++
  940. Routine Description:
  941. This routine checks that the security token has access for the specified
  942. RID (relative sub-authority) for the NT-authority SID
  943. Arguments:
  944. Token - security token
  945. Rid - well known relative sub-authority to check for presence in
  946. Return Value:
  947. none.
  948. --*/
  949. {
  950. BOOL b = FALSE;
  951. SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
  952. PSID Group;
  953. b = AllocateAndInitializeSid(
  954. &NtAuthority,
  955. 2,
  956. SECURITY_BUILTIN_DOMAIN_RID,
  957. Rid,
  958. 0, 0, 0, 0, 0, 0,
  959. &Group
  960. );
  961. if(b) {
  962. //
  963. // See if the user has the administrator group.
  964. //
  965. if (ImpersonateLoggedOnUser( Token )) {
  966. if (!CheckTokenMembership( NULL, Group, &b)) {
  967. b = FALSE;
  968. }
  969. RevertToSelf();
  970. } else {
  971. b = FALSE;
  972. }
  973. FreeSid(Group);
  974. }
  975. //
  976. // Clean up and return.
  977. //
  978. return b;
  979. }
  980. VOID
  981. SfcWLEventLogon(
  982. IN PWLX_NOTIFICATION_INFO pInfo
  983. )
  984. /*++
  985. Routine Description:
  986. This routine is called by winlogon each time a user logs onto the system.
  987. If a valid user is logged on, then we signal an event.
  988. Arguments:
  989. pInfo - pointer to WLX_NOTIFICATION_INFO structure filled in by winlogon
  990. Return Value:
  991. none.
  992. --*/
  993. {
  994. if (SfcWaitForValidDesktop()) {
  995. if (IsUserValid(pInfo->hToken,DOMAIN_ALIAS_RID_ADMINS)) {
  996. DebugPrint1(LVL_MINIMAL, L"user logged on = %ws",pInfo->UserName);
  997. UserLoggedOn = TRUE;
  998. hUserDesktop = pInfo->hDesktop;
  999. hUserToken = pInfo->hToken;
  1000. //
  1001. // record the user's name for later on
  1002. //
  1003. wcscpy( LoggedOnUserName, pInfo->UserName );
  1004. //
  1005. // now that a user is logged on, SFCNoPopups can transition to
  1006. // whatever value we grabbed on initialization (ie., we can now
  1007. // allow popups to occur since a user is logged on)
  1008. //
  1009. SFCNoPopUps = SFCNoPopUpsPolicy;
  1010. SetEvent( hEventLogon );
  1011. if ( SxsLogonEvent ) {
  1012. SxsLogonEvent();
  1013. }
  1014. } else {
  1015. DebugPrint1(
  1016. LVL_MINIMAL,
  1017. L"received a logon event, but user is not a member of domain administrators = %ws",
  1018. pInfo->UserName);
  1019. ;
  1020. }
  1021. }
  1022. }
  1023. VOID
  1024. SfcWLEventLogoff(
  1025. PWLX_NOTIFICATION_INFO pInfo
  1026. )
  1027. /*++
  1028. Routine Description:
  1029. This routine is called by winlogon each time a user logs off of the system.
  1030. We simply signal an event when this occurs. Note that the UserLoggedOff
  1031. global is set by a thread that will detect this event.
  1032. Arguments:
  1033. pInfo - pointer to WLX_NOTIFICATION_INFO structure filled in by winlogon
  1034. Return Value:
  1035. none.
  1036. --*/
  1037. {
  1038. BOOL ReallyLogoff;
  1039. DebugPrint1(LVL_MINIMAL, L"user logged off = %ws",pInfo->UserName);
  1040. ReallyLogoff = FALSE;
  1041. //
  1042. // See if the correct user logged off.
  1043. //
  1044. if (UserLoggedOn) {
  1045. if (_wcsicmp( LoggedOnUserName, pInfo->UserName )==0) {
  1046. ReallyLogoff = TRUE;
  1047. }
  1048. }
  1049. if (ReallyLogoff) {
  1050. //
  1051. // reset the logon event since the validation thread may not be around to do this
  1052. //
  1053. ResetEvent(hEventLogon);
  1054. //
  1055. // just fire the event if we had a valid user logged on
  1056. //
  1057. if (hEventLogoff) {
  1058. SetEvent( hEventLogoff );
  1059. if ( SxsLogoffEvent ) {
  1060. SxsLogoffEvent();
  1061. }
  1062. }
  1063. ASSERT((SFCSafeBootMode == 0)
  1064. ? (UserLoggedOn == TRUE)
  1065. : TRUE );
  1066. UserLoggedOn = FALSE;
  1067. hUserDesktop = NULL;
  1068. hUserToken = NULL;
  1069. LoggedOnUserName[0] = L'\0';
  1070. //
  1071. // now that the user is logged off, SFCNoPopups transitions to
  1072. // 1, meaning that we cannot allow any popups to occur until a
  1073. // user logs on again.
  1074. //
  1075. SFCNoPopUps = 1;
  1076. //
  1077. // we need to get rid of the persistant connection we asked the user to
  1078. // make earlier on
  1079. //
  1080. if (SFCLoggedOn == TRUE) {
  1081. WNetCancelConnection2( SFCNetworkLoginLocation, CONNECT_UPDATE_PROFILE, FALSE );
  1082. SFCLoggedOn = FALSE;
  1083. }
  1084. }
  1085. }
  1086. NTSTATUS
  1087. SfcQueueValidationThread(
  1088. IN PVOID lpv
  1089. )
  1090. /*++
  1091. Routine Description:
  1092. Thread routine that performs the file validations.
  1093. The validation thread can only run when a user is logged on.
  1094. The validation thread waits for an event which signals that there are
  1095. pending files to validate. It then cycles through this list of files,
  1096. validating each file that has not been validated.
  1097. If the file is valid, it is removed from the queue.
  1098. If the file is invalid, we first try to restore the file from cache.
  1099. If we cannot restore from cache, we try to determine if we'd require UI
  1100. to restore this file. We then have one of two global files we add the file
  1101. to (one which requires UI, one which does not). After we've gone through
  1102. the entire list of files, we will attempt to commit these queues if the
  1103. queue committal is not already in progress. (Care must be taken not to
  1104. add a file to a file queue that is already being committed.)
  1105. We then put the thread back to sleep waiting for a new event to wake up the
  1106. thread and start all over again. If we still have pending items to be
  1107. restored, we will put the thread back to sleep with a non-INFINITE timeout.
  1108. Arguments:
  1109. Unreferenced Parameter.
  1110. Return Value:
  1111. NTSTATUS code of any fatal error.
  1112. --*/
  1113. {
  1114. NTSTATUS Status;
  1115. HANDLE Handles[3];
  1116. PVALIDATION_REQUEST_DATA vrd;
  1117. HCATADMIN hCatAdmin = NULL;
  1118. LARGE_INTEGER Timeout;
  1119. PLARGE_INTEGER pTimeout = NULL;
  1120. HANDLE FileHandle;
  1121. #if 0
  1122. HDESK hDesk = NULL;
  1123. #endif
  1124. PSFC_REGISTRY_VALUE RegVal;
  1125. DWORD Ticks;
  1126. BOOL WaitAgain;
  1127. ULONG tmpErrorQueueCount;
  1128. PWSTR ActualFileNameOnMedia;
  1129. PLIST_ENTRY CurrentEntry;
  1130. BOOL RemoveEntry;
  1131. ULONG FilesNeedToBeCommited;
  1132. WCHAR InfFileName[MAX_PATH];
  1133. BOOL ExcepPackFile;
  1134. const DWORD cdwCatalogMinRetryTimeout = 30; // 30 seconds
  1135. const DWORD cdwCatalogMaxRetryTimeout = 128 * cdwCatalogMinRetryTimeout; // 64 minutes
  1136. DWORD dwCatalogRetryTimeout = cdwCatalogMinRetryTimeout;
  1137. UNREFERENCED_PARAMETER(lpv);
  1138. ASSERT((ValidateTermEvent != NULL)
  1139. && (ErrorQueueEvent != NULL)
  1140. && (hEventLogoff != NULL)
  1141. && (hEventLogon != NULL)
  1142. );
  1143. //
  1144. // if this thread has started, we'll need crypto; set the thread's ID before attempting to load it
  1145. //
  1146. g_dwValidationThreadID = GetCurrentThreadId();
  1147. Status = LoadCrypto();
  1148. if(!NT_SUCCESS(Status))
  1149. goto exit;
  1150. #if 0
  1151. //
  1152. // this thread must run on the user's desktop
  1153. //
  1154. hDesk = OpenInputDesktop( 0, FALSE, MAXIMUM_ALLOWED );
  1155. if ( hDesk ) {
  1156. SetThreadDesktop( hDesk );
  1157. CloseDesktop( hDesk );
  1158. }
  1159. #endif
  1160. //
  1161. // event tells us to stop validating (ie., machine is shutting down)
  1162. //
  1163. Handles[0] = ValidateTermEvent;
  1164. //
  1165. // tells us that new events were added to the validation queue
  1166. //
  1167. Handles[1] = ErrorQueueEvent;
  1168. //
  1169. // event tells us to start validating again since someone is logged on
  1170. //
  1171. Handles[2] = hEventLogon;
  1172. while (TRUE) {
  1173. //
  1174. // set our idle trigger to "signalled" if there are no events to be
  1175. // validated
  1176. //
  1177. if (hEventIdle && ErrorQueueCount == 0) {
  1178. SetEvent( hEventIdle );
  1179. }
  1180. //
  1181. // Wait for a change
  1182. //
  1183. Status = NtWaitForMultipleObjects(
  1184. sizeof(Handles)/sizeof(HANDLE),
  1185. Handles,
  1186. WaitAny,
  1187. TRUE,
  1188. pTimeout
  1189. );
  1190. if (!NT_SUCCESS(Status)) {
  1191. DebugPrint1( LVL_MINIMAL, L"WaitForMultipleObjects failed returning %x", Status );
  1192. goto exit;
  1193. }
  1194. DebugPrint1( LVL_VERBOSE,
  1195. L"SfcQueueValidationThread: WaitForMultipleObjects returned %x",
  1196. Status );
  1197. if (Status == 0) {
  1198. //
  1199. // the termination event fired so we must exit
  1200. //
  1201. goto exit;
  1202. }
  1203. //
  1204. // Make sure we acknowlege that the user logged on so this event
  1205. // doesn't remain signalled forever
  1206. //
  1207. if ( (Status == 2) || (Status == 1) ) {
  1208. if (WaitForSingleObject(hEventLogon,0) == WAIT_OBJECT_0) {
  1209. //
  1210. // the logon event fired
  1211. //
  1212. ASSERT(UserLoggedOn == TRUE);
  1213. ResetEvent( hEventLogon );
  1214. if (Status == 2) {
  1215. if (IsListEmpty(&SfcErrorQueue)) {
  1216. DebugPrint(
  1217. LVL_MINIMAL,
  1218. L"logon event but queue is empty...");
  1219. pTimeout = NULL;
  1220. } else {
  1221. DebugPrint(
  1222. LVL_MINIMAL,
  1223. L"logon event occurred with requests in the queue. see if we can satisfy any of the requests");
  1224. pTimeout = &Timeout;
  1225. goto validate_start;
  1226. }
  1227. continue;
  1228. }
  1229. } else {
  1230. ASSERT(Status == 1);
  1231. }
  1232. }
  1233. if (Status == STATUS_TIMEOUT) {
  1234. //
  1235. // a timeout is necessary only when there are entries in the list
  1236. // that are servicable
  1237. //
  1238. if (IsListEmpty(&SfcErrorQueue)) {
  1239. DebugPrint(LVL_MINIMAL, L"Timeout in SfcQueueValidationThread but queue is empty");
  1240. pTimeout = NULL;
  1241. } else {
  1242. DebugPrint(LVL_MINIMAL, L"Timeout in SfcQueueValidationThread with requests in the queue. check it out");
  1243. pTimeout = &Timeout;
  1244. goto validate_start;
  1245. }
  1246. continue;
  1247. }
  1248. if (Status > sizeof(Handles)/sizeof(HANDLE)) {
  1249. DebugPrint1( LVL_MINIMAL, L"Unknown success code %d for WaitForMultipleObjects", Status );
  1250. continue;
  1251. }
  1252. ASSERT(Status == 1);
  1253. validate_start:
  1254. DebugPrint( LVL_MINIMAL, L"Processing queued file validations..." );
  1255. //
  1256. // process any file validations
  1257. //
  1258. //
  1259. // Reset our "idle trigger so that it is unsignalled while we validate
  1260. // the files
  1261. //
  1262. if (hEventIdle) {
  1263. ResetEvent( hEventIdle );
  1264. }
  1265. NtResetEvent( ErrorQueueEvent, NULL );
  1266. ASSERT(hCatAdmin == NULL);
  1267. if (!CryptCATAdminAcquireContext(&hCatAdmin, &DriverVerifyGuid, 0)) {
  1268. DebugPrint1( LVL_MINIMAL, L"CCAAC() failed, ec=%d", GetLastError() );
  1269. hCatAdmin = NULL;
  1270. //
  1271. // try again to get a context; each time, double the timeout until we reach the max
  1272. //
  1273. Timeout.QuadPart = (1000 * dwCatalogRetryTimeout) * (-10000);
  1274. pTimeout = &Timeout;
  1275. if(dwCatalogRetryTimeout < cdwCatalogMaxRetryTimeout)
  1276. {
  1277. dwCatalogRetryTimeout *= 2;
  1278. }
  1279. continue;
  1280. }
  1281. //
  1282. // reset the catalog wait timeout
  1283. //
  1284. dwCatalogRetryTimeout = cdwCatalogMinRetryTimeout;
  1285. //
  1286. // Flush the Cache once before we start any Crypto operations
  1287. //
  1288. SfcFlushCryptoCache();
  1289. //
  1290. // Refresh exception packages info
  1291. //
  1292. SfcRefreshExceptionInfo();
  1293. Timeout.QuadPart = (1000*SFC_QUEUE_WAIT) * (-10000);
  1294. WaitAgain = FALSE;
  1295. //
  1296. // cycle through the list of queued files, processing one at a time
  1297. // until we get back to the start again
  1298. //
  1299. CurrentEntry = SfcErrorQueue.Flink;
  1300. while (CurrentEntry != &SfcErrorQueue) {
  1301. RemoveEntry = FALSE;
  1302. DebugPrint1( LVL_VERBOSE,
  1303. L"CurrentEntry= %p",
  1304. CurrentEntry );
  1305. ASSERT(ErrorQueueCount > 0 );
  1306. //
  1307. // get the current entry from the list and point to the next entry
  1308. //
  1309. RtlEnterCriticalSection( &ErrorCs );
  1310. vrd = CONTAINING_RECORD( CurrentEntry, VALIDATION_REQUEST_DATA, Entry );
  1311. RegVal = vrd->RegVal;
  1312. ASSERT(RegVal != NULL);
  1313. CurrentEntry = CurrentEntry->Flink;
  1314. RtlLeaveCriticalSection( &ErrorCs );
  1315. DebugPrint2( LVL_VERBOSE,
  1316. L"Processing validation request for [%wZ], flags = 0x%08x ",
  1317. &vrd->RegVal->FullPathName,
  1318. vrd->Flags );
  1319. //
  1320. // attempt to validate the file if we haven't already done so
  1321. //
  1322. if ((vrd->Flags & VRD_FLAG_REQUEST_PROCESSED) == 0) {
  1323. //
  1324. // skip this file if the queue has not paused long enough
  1325. //
  1326. Ticks = GetTickCount();
  1327. ASSERT(vrd->NextValidTime != 0);
  1328. if (vrd->NextValidTime && Ticks < vrd->NextValidTime) {
  1329. Timeout.QuadPart = (__int64)(vrd->NextValidTime - Ticks) * -10000;
  1330. WaitAgain = TRUE;
  1331. RemoveEntry = FALSE;
  1332. DebugPrint1( LVL_VERBOSE,
  1333. L"Have not waited long enough on validation request for [%wZ]",
  1334. &vrd->RegVal->FullPathName );
  1335. goto continue_entry;
  1336. }
  1337. //
  1338. // see if we can open the file, otherwise wait a bit until we have
  1339. // a chance to validate the file.
  1340. //
  1341. Status = SfcOpenFile( &vrd->RegVal->FileName, vrd->RegVal->DirHandle, FILE_SHARE_READ, &FileHandle );
  1342. if (NT_SUCCESS(Status) ) {
  1343. DebugPrint1( LVL_VERBOSE, L"file opened successfully [%wZ] ", &vrd->RegVal->FileName );
  1344. NtClose( FileHandle );
  1345. } else {
  1346. if (Status == STATUS_SHARING_VIOLATION) {
  1347. DebugPrint1( LVL_VERBOSE, L"file sharing violation [%wZ] ", &vrd->RegVal->FileName );
  1348. vrd->RetryCount += 1;
  1349. RemoveEntry = FALSE;
  1350. WaitAgain = TRUE;
  1351. goto continue_entry;
  1352. }
  1353. }
  1354. //
  1355. // now validate the file
  1356. //
  1357. SfcValidateDLL( vrd, hCatAdmin );
  1358. vrd->Flags |= VRD_FLAG_REQUEST_PROCESSED;
  1359. }
  1360. ASSERT((vrd->Flags & VRD_FLAG_REQUEST_PROCESSED)==VRD_FLAG_REQUEST_PROCESSED);
  1361. //
  1362. // if the file is valid, we're ready to go onto the next file
  1363. //
  1364. if (vrd->ImageValData.Original.SignatureValid) {
  1365. //
  1366. // before we continue, let's see if we can synchronize the copy
  1367. // of the file in the dll cache
  1368. //
  1369. if (!SfcSyncCache( vrd, hCatAdmin )) {
  1370. DebugPrint1( LVL_VERBOSE,
  1371. L"failed to synchronize [%wZ] in dllcache",
  1372. &vrd->RegVal->FileName );
  1373. }
  1374. RemoveEntry = TRUE;
  1375. goto continue_next;
  1376. }
  1377. ASSERT(vrd->ImageValData.Original.SignatureValid == FALSE);
  1378. //
  1379. // see if we can restore from cache. If this succeeds, we're
  1380. // ready to go onto the next file
  1381. //
  1382. if (vrd->ImageValData.Cache.SignatureValid) {
  1383. SfcRestoreFromCache( vrd, hCatAdmin );
  1384. if (vrd->CopyCompleted) {
  1385. DebugPrint1( LVL_VERBOSE,
  1386. L"File [%wZ] was restored from cache",
  1387. &vrd->RegVal->FileName );
  1388. RemoveEntry = TRUE;
  1389. goto continue_next;
  1390. }
  1391. }
  1392. if ((vrd->Flags & VRD_FLAG_REQUEST_QUEUED) == 0) {
  1393. //
  1394. // check if the file is available. If it is, then we can restore
  1395. // it without any UI coming up
  1396. //
  1397. ActualFileNameOnMedia = FileNameOnMedia( RegVal );
  1398. //
  1399. // get the inf name here
  1400. //
  1401. ExcepPackFile = SfcGetInfName(RegVal, InfFileName);
  1402. //
  1403. // get the source information which let's us know where to look
  1404. // for the file
  1405. //
  1406. wcscpy(vrd->SourceInfo.SourceFileName,ActualFileNameOnMedia);
  1407. if (!SfcGetSourceInformation(ActualFileNameOnMedia,InfFileName,ExcepPackFile,&vrd->SourceInfo)) {
  1408. //
  1409. // if this fails, just assume that we need UI
  1410. //
  1411. vrd->Flags |= VRD_FLAG_REQUIRE_UI;
  1412. } else {
  1413. WCHAR DontCare[MAX_PATH];
  1414. WCHAR FilePath[MAX_PATH];
  1415. WCHAR SourcePath[MAX_PATH];
  1416. SOURCE_MEDIA SourceMedia;
  1417. DWORD Result;
  1418. RtlZeroMemory( &SourceMedia, sizeof( SourceMedia ));
  1419. //
  1420. // build up a temporary SOURCE_MEDIA structure as well
  1421. // as the full path to the file we're looking for
  1422. //
  1423. wcscpy( SourcePath, vrd->SourceInfo.SourceRootPath );
  1424. pSetupConcatenatePaths(
  1425. SourcePath,
  1426. vrd->SourceInfo.SourcePath,
  1427. UnicodeChars(SourcePath),
  1428. NULL);
  1429. //
  1430. // note the wierd syntax here because TAGFILE is a macro
  1431. // that accepts a PSOURCE_INFO pointer.
  1432. //
  1433. SourceMedia.Tagfile = TAGFILE(((PSOURCE_INFO)&vrd->SourceInfo));
  1434. SourceMedia.Description = vrd->SourceInfo.Description;
  1435. SourceMedia.SourcePath = SourcePath;
  1436. SourceMedia.SourceFile = ActualFileNameOnMedia;
  1437. wcscpy( FilePath, vrd->SourceInfo.SourceRootPath );
  1438. BuildPathForFile(
  1439. vrd->SourceInfo.SourceRootPath,
  1440. vrd->SourceInfo.SourcePath,
  1441. ActualFileNameOnMedia,
  1442. SFC_INCLUDE_SUBDIRECTORY,
  1443. SFC_INCLUDE_ARCHSUBDIR,
  1444. FilePath,
  1445. UnicodeChars(FilePath) );
  1446. Result = SfcQueueLookForFile(
  1447. &SourceMedia,
  1448. &vrd->SourceInfo,
  1449. FilePath,
  1450. DontCare);
  1451. if (Result == FILEOP_ABORT) {
  1452. vrd->Flags |= VRD_FLAG_REQUIRE_UI;
  1453. }
  1454. }
  1455. vrd->SourceInfo.ValidationRequestData = vrd;
  1456. //
  1457. // now add the file to the proper file queue
  1458. //
  1459. if (SfcQueueAddFileToRestoreQueue(
  1460. vrd->Flags & VRD_FLAG_REQUIRE_UI,
  1461. RegVal,
  1462. InfFileName,
  1463. ExcepPackFile,
  1464. &vrd->SourceInfo,
  1465. ActualFileNameOnMedia)) {
  1466. vrd->Flags |= VRD_FLAG_REQUEST_QUEUED;
  1467. } else {
  1468. //
  1469. // we failed for some reason. put the request back in
  1470. // the queue to see if we can add it later on
  1471. //
  1472. WaitAgain = TRUE;
  1473. goto continue_entry;
  1474. }
  1475. }
  1476. continue_entry:
  1477. if (vrd->RetryCount < 10) {
  1478. NOTHING;
  1479. } else {
  1480. DebugPrint1( LVL_MINIMAL, L"Could not restore file [%ws], retries exceeded", RegVal->FileName.Buffer);
  1481. RemoveEntry = TRUE;
  1482. SfcReportEvent(
  1483. MSG_RESTORE_FAILURE_MAX_RETRIES,
  1484. RegVal->FileName.Buffer,
  1485. NULL,
  1486. 0 );
  1487. }
  1488. continue_next:
  1489. if (RemoveEntry) {
  1490. //
  1491. // remove the entry if we're done with it. otherwise we leave
  1492. // it in the list just in case we get more notifications about
  1493. // this file
  1494. //
  1495. RtlEnterCriticalSection( &ErrorCs );
  1496. RemoveEntryList( &vrd->Entry );
  1497. ErrorQueueCount -= 1;
  1498. RtlLeaveCriticalSection( &ErrorCs );
  1499. MemFree( vrd );
  1500. }
  1501. } // end of while(CurrentEntry != &SfcErrorQueue)
  1502. //
  1503. // we've cycled through the validation queue. now if we've queued any
  1504. // files for restoration, we can do that now.
  1505. //
  1506. //
  1507. // no UI filequeue
  1508. //
  1509. SfcQueueCommitRestoreQueue( FALSE );
  1510. SfcQueueResetQueue( FALSE );
  1511. if (UserLoggedOn) {
  1512. //
  1513. // UI filequeue
  1514. //
  1515. SfcQueueCommitRestoreQueue( TRUE );
  1516. SfcQueueResetQueue( TRUE );
  1517. } else {
  1518. RtlEnterCriticalSection( &UIRestoreQueue.CriticalSection );
  1519. RtlEnterCriticalSection( &ErrorCs );
  1520. tmpErrorQueueCount = ErrorQueueCount;
  1521. RtlLeaveCriticalSection( &ErrorCs );
  1522. FilesNeedToBeCommited = UIRestoreQueue.QueueCount;
  1523. RtlLeaveCriticalSection( &UIRestoreQueue.CriticalSection );
  1524. }
  1525. //
  1526. // getting ready to wait again, cleanup our admin context
  1527. //
  1528. if (hCatAdmin) {
  1529. CryptCATAdminReleaseContext(hCatAdmin,0);
  1530. hCatAdmin = NULL;
  1531. }
  1532. //
  1533. // a timeout is necessary only when there are
  1534. // entries in the list that can be acted on
  1535. //
  1536. // if all of our files need UI but the user is not logged on, then
  1537. // we should just sleep until the user logs on again.
  1538. //
  1539. if (IsListEmpty(&SfcErrorQueue) ||
  1540. (UserLoggedOn == FALSE
  1541. && tmpErrorQueueCount == FilesNeedToBeCommited) ||
  1542. (WaitAgain == FALSE) ) {
  1543. pTimeout = NULL;
  1544. } else {
  1545. pTimeout = &Timeout;
  1546. }
  1547. }
  1548. exit:
  1549. //
  1550. // we're terminating our validation thread. remember this so we don't
  1551. // start a new thread up.
  1552. //
  1553. ShuttingDown = TRUE;
  1554. //
  1555. // Log an event for each validation request we couldn't finish servicing.
  1556. // This will at least let the user know that they should run a scan on
  1557. // their system.
  1558. //
  1559. pSfcHandleAllOrphannedRequests();
  1560. DebugPrint( LVL_MINIMAL, L"SfcQueueValidationThread terminating" );
  1561. return 0;
  1562. }
  1563. NTSTATUS
  1564. SfcQueueValidationRequest(
  1565. IN PSFC_REGISTRY_VALUE RegVal,
  1566. IN ULONG ChangeType
  1567. )
  1568. /*++
  1569. Routine Description:
  1570. Routine adds a new validation request to the validation queue.
  1571. It is called by the watcher thread to add the new validation request.
  1572. This routine must be as fast as possible because we want to start watching
  1573. the directory for other changes as soon as possible.
  1574. Arguments:
  1575. RegVal - pointer to the SFC_REGISTRY_VALUE for the file that should be
  1576. validated.
  1577. ChangeType - type of change that occurred to the file.
  1578. Return Value:
  1579. NTSTATUS code indicating outcome.
  1580. --*/
  1581. {
  1582. NTSTATUS Status = STATUS_SUCCESS;
  1583. PVALIDATION_REQUEST_DATA vrd;
  1584. PVALIDATION_REQUEST_DATA vrdexisting;
  1585. ASSERT(RegVal != NULL);
  1586. //
  1587. // if we're in GUI-Setup, don't queue any validation requests
  1588. //
  1589. if (SFCDisable == SFC_DISABLE_SETUP) {
  1590. return STATUS_SUCCESS;
  1591. }
  1592. //
  1593. // allocate a vrd & initialize it
  1594. //
  1595. vrd = MemAlloc( sizeof(VALIDATION_REQUEST_DATA) );
  1596. if (vrd == NULL) {
  1597. DebugPrint( LVL_MINIMAL,
  1598. L"SfcQueueValidationRequest failed to allocate memory for validation request" );
  1599. return STATUS_NO_MEMORY;
  1600. }
  1601. vrd->NextValidTime = GetTickCount() + (1000*SFCStall);
  1602. vrd->RegVal = RegVal;
  1603. vrd->ChangeType = ChangeType;
  1604. vrd->Signature = SFC_VRD_SIGNATURE;
  1605. //
  1606. // insert it in the list if it isn't already in the list. Note that we
  1607. // take the hit of looking through this list right now for this file
  1608. // because it's much simpler and faster later on if we don't have any
  1609. // duplicates in our list
  1610. //
  1611. //
  1612. // note that we do allow a duplicate entry in the list if the file has
  1613. // already been queued for restoration (if someone changes a file after
  1614. // we restore it but before we remove it from the queue, we don't want
  1615. // there to be a window where we don't care if the file changes). We
  1616. // ignore this window in the case of restoring from cache because that
  1617. // is a much quicker codepath.
  1618. //
  1619. // Note that the reasoning above is incorrect, in that the window of time
  1620. // in the cache restore case, though much quicker than the restore from
  1621. // media case, can be significant. So this logic is changed to say that
  1622. // we remove duplicate entries in the case that we haven't yet verified
  1623. // the file's signature. Once we verify the signature of the file and get
  1624. // a change notification, we need another request to re-verify the file.
  1625. //
  1626. //
  1627. RtlEnterCriticalSection( &ErrorCs );
  1628. vrdexisting = IsFileInQueue( RegVal);
  1629. if (!vrdexisting || (vrdexisting->Flags & VRD_FLAG_REQUEST_PROCESSED) ) {
  1630. DebugPrint1( LVL_VERBOSE,
  1631. L"Inserting [%ws] into error queue for validation",
  1632. RegVal->FullPathName.Buffer );
  1633. InsertTailList( &SfcErrorQueue, &vrd->Entry );
  1634. ErrorQueueCount += 1;
  1635. //
  1636. // do this to avoid free later on
  1637. //
  1638. vrdexisting = NULL;
  1639. } else {
  1640. vrd->NextValidTime = GetTickCount() + (1000*SFCStall);
  1641. DebugPrint1( LVL_VERBOSE,
  1642. L"Not inserting [%ws] into error queue for validation. (The file is already present in the error validation queue.)",
  1643. RegVal->FullPathName.Buffer );
  1644. }
  1645. //
  1646. // create the list processor thread, if necessary
  1647. //
  1648. if (hErrorThread == NULL) {
  1649. Status = NtCreateEvent(
  1650. &ErrorQueueEvent,
  1651. EVENT_ALL_ACCESS,
  1652. NULL,
  1653. NotificationEvent,
  1654. FALSE
  1655. );
  1656. if (NT_SUCCESS(Status)) {
  1657. //
  1658. // Create error queue thread
  1659. //
  1660. hErrorThread = CreateThread(
  1661. NULL,
  1662. 0,
  1663. SfcQueueValidationThread,
  1664. 0,
  1665. 0,
  1666. NULL
  1667. );
  1668. if (hErrorThread == NULL) {
  1669. DebugPrint1( LVL_MINIMAL, L"Unable to create error queue thread, ec=%d", GetLastError() );
  1670. Status = STATUS_UNSUCCESSFUL;
  1671. }
  1672. } else {
  1673. DebugPrint1( LVL_MINIMAL, L"Unable to create error queue event, ec=0x%08x", Status );
  1674. }
  1675. }
  1676. RtlLeaveCriticalSection( &ErrorCs );
  1677. //
  1678. // signal an event to the validation thread to wakeup and process the
  1679. // request
  1680. //
  1681. if (NT_SUCCESS(Status)) {
  1682. ASSERT(hErrorThread != NULL);
  1683. RtlEnterCriticalSection( &ErrorCs );
  1684. NtSetEvent(ErrorQueueEvent,NULL);
  1685. RtlLeaveCriticalSection( &ErrorCs );
  1686. }
  1687. //
  1688. // if we already inserted an event into the list, we don't need this
  1689. // entry anymore, so free it
  1690. //
  1691. if (vrdexisting) {
  1692. MemFree( vrd );
  1693. }
  1694. return Status;
  1695. }
  1696. BOOL
  1697. SfcGetValidationData(
  1698. IN PUNICODE_STRING FileName,
  1699. IN PUNICODE_STRING FullPathName,
  1700. IN HANDLE DirHandle,
  1701. IN HCATADMIN hCatAdmin,
  1702. OUT PIMAGE_VALIDATION_DATA ImageValData
  1703. )
  1704. /*++
  1705. Routine Description:
  1706. Routine takes a filename in a given directory and fills in an
  1707. IMAGE_VALIDATION_DATA structure based on it's checks
  1708. Arguments:
  1709. FileName - unicode_string containing file to be checked
  1710. FullPathName - unicode_string containing fully qualified path name of file
  1711. DirHandle - handle to directory the file is located in
  1712. hCatAdmin - crypto context handle to be used in checking file
  1713. ImageValData - pointer to IMAGE_VALIDATION_DATA structure
  1714. Return Value:
  1715. TRUE indicates the file data was successfully retreived.
  1716. --*/
  1717. {
  1718. NTSTATUS Status;
  1719. HANDLE FileHandle;
  1720. ASSERT((FileName != NULL) && (FileName->Buffer != NULL));
  1721. ASSERT((FullPathName != NULL) && (FullPathName->Buffer != NULL));
  1722. ASSERT( (DirHandle != NULL)
  1723. && (hCatAdmin != NULL)
  1724. && (ImageValData != NULL) );
  1725. RtlZeroMemory( ImageValData, sizeof(IMAGE_VALIDATION_DATA) );
  1726. //
  1727. // open the file
  1728. //
  1729. Status = SfcOpenFile( FileName, DirHandle, SHARE_ALL, &FileHandle );
  1730. if (NT_SUCCESS(Status)) {
  1731. ASSERT(FileHandle != INVALID_HANDLE_VALUE);
  1732. ImageValData->FilePresent = TRUE;
  1733. SfcGetFileVersion(FileHandle,
  1734. &ImageValData->DllVersion,
  1735. &ImageValData->DllCheckSum,
  1736. ImageValData->FileName );
  1737. } else {
  1738. //
  1739. // we don't to anything on failure since this is an expected state
  1740. // if the file was just removed. The member variables's below are
  1741. // automatically set at the entrypoint to the function so they are
  1742. // not necessary but are present and commented out for the sake of
  1743. // clarity
  1744. //
  1745. NOTHING;
  1746. //ImageValData->SignatureValid = FALSE;
  1747. //ImageValData->FilePresent = FALSE;
  1748. }
  1749. //
  1750. // verify the file signature
  1751. //
  1752. if (hCatAdmin && FileHandle != NULL) {
  1753. ImageValData->SignatureValid = SfcValidateFileSignature(
  1754. hCatAdmin,
  1755. FileHandle,
  1756. FileName->Buffer,
  1757. FullPathName->Buffer);
  1758. } else {
  1759. ImageValData->SignatureValid = FALSE;
  1760. }
  1761. //
  1762. // close the file
  1763. //
  1764. if (FileHandle != INVALID_HANDLE_VALUE) {
  1765. NtClose( FileHandle );
  1766. }
  1767. return TRUE;
  1768. }
  1769. BOOL
  1770. SfcValidateDLL(
  1771. IN PVALIDATION_REQUEST_DATA vrd,
  1772. IN HCATADMIN hCatAdmin
  1773. )
  1774. /*++
  1775. Routine Description:
  1776. Routine takes a validation request and processes it.
  1777. It does this by checking if the file is present, and if so, it checks the
  1778. signature of the file.
  1779. This routine does not replace any files, it merely checks the signature of
  1780. the file and of the copy of the file in the cache, if present.
  1781. Arguments:
  1782. vrd - pointer to VALIDATION_REQUEST_DATA structure describing the file to
  1783. be checked.
  1784. hCatAdmin - crypto context handle to be used in checking file
  1785. Return Value:
  1786. always TRUE (indicates we successfully validated the DLL as good or bad)
  1787. --*/
  1788. {
  1789. PSFC_REGISTRY_VALUE RegVal = vrd->RegVal;
  1790. PCOMPLETE_VALIDATION_DATA ImageValData = &vrd->ImageValData;
  1791. UNICODE_STRING ActualFileName;
  1792. PCWSTR FileName;
  1793. //
  1794. // get the version information for both files (the cached version and the
  1795. // current version)
  1796. //
  1797. SfcGetValidationData( &RegVal->FileName,
  1798. &RegVal->FullPathName,
  1799. RegVal->DirHandle,
  1800. hCatAdmin,
  1801. &ImageValData->Original);
  1802. {
  1803. UNICODE_STRING FullPath;
  1804. WCHAR Buffer[MAX_PATH];
  1805. RtlZeroMemory( &ImageValData->Cache, sizeof(IMAGE_VALIDATION_DATA) );
  1806. FileName = FileNameOnMedia( RegVal );
  1807. RtlInitUnicodeString( &ActualFileName, FileName );
  1808. ASSERT(FileName != NULL);
  1809. wcscpy(Buffer, SfcProtectedDllPath.Buffer);
  1810. pSetupConcatenatePaths( Buffer, ActualFileName.Buffer, UnicodeChars(Buffer), NULL);
  1811. RtlInitUnicodeString( &FullPath, Buffer );
  1812. SfcGetValidationData( &ActualFileName,
  1813. &FullPath,
  1814. SfcProtectedDllFileDirectory,
  1815. hCatAdmin,
  1816. &ImageValData->Cache);
  1817. }
  1818. DebugPrint8( LVL_VERBOSE, L"Version Data (%wZ),(%ws) - %I64x, %I64x, %lx, %lx (%ws) >%ws<",
  1819. &RegVal->FileName,
  1820. ImageValData->Original.FileName,
  1821. ImageValData->Original.DllVersion,
  1822. ImageValData->Cache.DllVersion,
  1823. ImageValData->Original.DllCheckSum,
  1824. ImageValData->Cache.DllCheckSum,
  1825. ImageValData->Original.FilePresent ? L"Present" : L"Missing",
  1826. ImageValData->Original.SignatureValid ? L"good" : L"bad"
  1827. );
  1828. //
  1829. // log the fact that the file was validated
  1830. //
  1831. #ifdef SFCCHANGELOG
  1832. if (SFCChangeLog) {
  1833. SfcLogFileWrite( IDS_FILE_CHANGE, RegVal->FileName.Buffer );
  1834. }
  1835. #endif
  1836. return TRUE;
  1837. }
  1838. BOOL
  1839. pSfcHandleAllOrphannedRequests(
  1840. VOID
  1841. )
  1842. /*++
  1843. Routine Description:
  1844. This function cycles through the list of validation requests, taking an
  1845. action (for now, just logging an event) for each request, then removing
  1846. the request
  1847. Arguments: None.
  1848. Return Value: TRUE indicates that all requests were successfully removed. If
  1849. any request could not be closed, the return value is FALSE.
  1850. --*/
  1851. {
  1852. PLIST_ENTRY Current;
  1853. PVALIDATION_REQUEST_DATA vrd;
  1854. BOOL RetVal = TRUE;
  1855. DWORD Total;
  1856. RtlEnterCriticalSection( &ErrorCs );
  1857. Total = ErrorQueueCount;
  1858. Current = SfcErrorQueue.Flink;
  1859. while (Current != &SfcErrorQueue) {
  1860. vrd = CONTAINING_RECORD( Current, VALIDATION_REQUEST_DATA, Entry );
  1861. ASSERT( vrd->Signature == SFC_VRD_SIGNATURE );
  1862. ASSERT( vrd->RegVal != NULL );
  1863. Current = Current->Flink;
  1864. SfcReportEvent(
  1865. MSG_DLL_NOVALIDATION_TERMINATION,
  1866. vrd->RegVal->FullPathName.Buffer,
  1867. NULL,
  1868. 0 );
  1869. ErrorQueueCount -= 1;
  1870. RemoveEntryList( &vrd->Entry );
  1871. MemFree( vrd );
  1872. }
  1873. RtlLeaveCriticalSection( &ErrorCs );
  1874. ASSERT( ErrorQueueCount == 0 );
  1875. return(RetVal);
  1876. }
  1877. BOOL
  1878. SfcWaitForValidDesktop(
  1879. VOID
  1880. )
  1881. {
  1882. HDESK hDesk = NULL;
  1883. WCHAR DesktopName[128];
  1884. DWORD BytesNeeded;
  1885. DWORD i;
  1886. BOOL RetVal = FALSE;
  1887. #define MAX_DESKTOP_RETRY_COUNT 60
  1888. if (hEventLogon) {
  1889. //
  1890. // open a handle to the desktop and check if the current desktop is the
  1891. // default desktop. If it isn't then wait for the desktop event to be
  1892. // signalled before proceeding
  1893. //
  1894. // Note that this event is pulsed so we have a timeout loop in case the
  1895. // event isn't signalled while we are waiting for it and the desktop
  1896. // transitions from the winlogon desktop to the default desktop
  1897. //
  1898. i = 0;
  1899. try_again:
  1900. ASSERT( hDesk == NULL );
  1901. hDesk = OpenInputDesktop( 0, FALSE, MAXIMUM_ALLOWED );
  1902. if (GetUserObjectInformation( hDesk, UOI_NAME, DesktopName, sizeof(DesktopName), &BytesNeeded )) {
  1903. if (wcscmp( DesktopName, L"Default" )) {
  1904. if (WaitForSingleObject( hEventDeskTop, 1000 * 2 ) == WAIT_TIMEOUT) {
  1905. i += 1;
  1906. if (i < MAX_DESKTOP_RETRY_COUNT) {
  1907. if (hDesk) {
  1908. CloseDesktop( hDesk );
  1909. hDesk = NULL;
  1910. }
  1911. goto try_again;
  1912. }
  1913. } else {
  1914. RetVal = TRUE;
  1915. }
  1916. } else {
  1917. RetVal = TRUE;
  1918. }
  1919. }
  1920. if (hDesk) {
  1921. CloseDesktop( hDesk );
  1922. }
  1923. }
  1924. return(RetVal);
  1925. }