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.

1346 lines
37 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. qfecheck.c
  5. Abstract:
  6. Author:
  7. Alan Back (alanbac) 11-30-00
  8. Environment:
  9. Windows 2000
  10. Revision History:
  11. --*/
  12. #include <nt.h>
  13. #include <ntrtl.h>
  14. #include <nturtl.h>
  15. #include <ctype.h>
  16. #include <stdio.h>
  17. #include <windows.h> // includes basic windows functionality
  18. #include <string.h> // includes the string functions
  19. #include <stdlib.h>
  20. #include <imagehlp.h>
  21. #include "setupapi.h" // includes the inf setup api
  22. #include "spapip.h"
  23. #include <tchar.h>
  24. #include <mscat.h>
  25. #include <softpub.h>
  26. #include "qfecheck.h"
  27. #define MISC_BUF_SIZE 4096
  28. //
  29. // Globals
  30. //
  31. CHAR MiscBuffer2[ MISC_BUF_SIZE * 2];
  32. CHAR System32Directory[ MAX_PATH ];
  33. CHAR CmdLineLocation[MAX_PATH * 2 ];
  34. CHAR ComputerName[ MAX_PATH ];
  35. HANDLE LogFile = NULL;
  36. BOOL QuietMode = FALSE;
  37. BOOL Verbose = FALSE;
  38. BOOL DoLogging = FALSE;
  39. OSVERSIONINFOEX osvi;
  40. GUID DriverVerifyGuid = DRIVER_ACTION_VERIFY;
  41. //
  42. // pointers to the crypto functions we call
  43. //
  44. PCRYPTCATADMINACQUIRECONTEXT pCryptCATAdminAcquireContext;
  45. PCRYPTCATADMINRELEASECONTEXT pCryptCATAdminReleaseContext;
  46. PCRYPTCATADMINCALCHASHFROMFILEHANDLE pCryptCATAdminCalcHashFromFileHandle;
  47. PCRYPTCATADMINENUMCATALOGFROMHASH pCryptCATAdminEnumCatalogFromHash;
  48. PCRYPTCATCATALOGINFOFROMCONTEXT pCryptCATCatalogInfoFromContext;
  49. PCRYPTCATADMINRELEASECATALOGCONTEXT pCryptCATAdminReleaseCatalogContext;
  50. PWINVERIFYTRUST pWinVerifyTrust;
  51. PMULTIBYTETOUNICODE pMultiByteToUnicode;
  52. typedef HANDLE (WINAPI *CONNECTTOSFCSERVER)(PCWSTR);
  53. typedef BOOL (WINAPI *ISFILEPROTECTED)(HANDLE, LPCWSTR);
  54. typedef VOID (WINAPI *CLOSESFC)(HANDLE);
  55. VOID _cdecl
  56. main(
  57. int argc,
  58. char * argv[]
  59. )
  60. {
  61. CHAR MiscBuffer[ MISC_BUF_SIZE ];
  62. LPSTR p;
  63. CHAR szSourcePath[ MAX_PATH ];
  64. CHAR LogName[ MAX_PATH ];
  65. DWORD LogNameSize = sizeof( LogName );
  66. //
  67. // Get the command line stuff
  68. //
  69. if ( !ParseArgs( argc, argv )) {
  70. LoadString(NULL, STR_USAGE, MiscBuffer, sizeof(MiscBuffer));
  71. PrintStringToConsole( MiscBuffer );
  72. return;
  73. }
  74. //
  75. // Get Version and SP info
  76. //
  77. osvi.dwOSVersionInfoSize = sizeof( osvi );
  78. GetVersionEx( (LPOSVERSIONINFO)&osvi );
  79. //
  80. // Don't run on anything less than Windows 2000
  81. //
  82. if ( osvi.dwMajorVersion < 5 ) {
  83. LoadString(NULL, STR_USAGE, MiscBuffer, sizeof(MiscBuffer));
  84. PrintStringToConsole( MiscBuffer );
  85. return;
  86. }
  87. if ( !GetSystemDirectory( System32Directory, sizeof( System32Directory ))) {
  88. LoadString(NULL, STR_NO_SYSDIR, MiscBuffer, sizeof(MiscBuffer));
  89. PrintStringToConsole( MiscBuffer );
  90. return;
  91. }
  92. //
  93. // Get the computername and use it for the log file name.
  94. // If the user input a path location for the logfile, use it.
  95. // Otherwise, use the current directory (or failing that, system32).
  96. //
  97. if ( !GetComputerName( ComputerName, &LogNameSize )) {
  98. LoadString(NULL, STR_GETCOMPUTERNAME_FAILED, MiscBuffer, sizeof(MiscBuffer));
  99. PrintStringToConsole( MiscBuffer );
  100. return;
  101. }
  102. if ( DoLogging ) {
  103. strcpy( LogName, ComputerName );
  104. strcat( LogName, ".log" );
  105. if ( CmdLineLocation[0] ) {
  106. if (( GetFileAttributes( CmdLineLocation ) & FILE_ATTRIBUTE_DIRECTORY ) == 0 ) {
  107. LoadString(NULL, STR_USAGE, MiscBuffer, sizeof(MiscBuffer));
  108. PrintStringToConsole( MiscBuffer );
  109. return;
  110. }
  111. CombinePaths( CmdLineLocation, LogName, szSourcePath );
  112. } else {
  113. GetModuleFileName( NULL, szSourcePath, sizeof( szSourcePath ));
  114. p = strrchr( szSourcePath, '\\' );
  115. if ( p ) {
  116. *p = 0;
  117. } else {
  118. strcpy( szSourcePath, System32Directory );
  119. }
  120. CombinePaths( szSourcePath, LogName, szSourcePath );
  121. }
  122. //
  123. // Initialize the logfile using the machinename for the logfilename.
  124. //
  125. if ( !InitializeLog( TRUE, szSourcePath ) ) {
  126. LoadString(NULL, STR_LOGFILE_INIT_FAILED, MiscBuffer, sizeof(MiscBuffer));
  127. PrintStringToConsole( MiscBuffer );
  128. return;
  129. }
  130. }
  131. //
  132. // OK, now check that each of the files on the system match
  133. // what the hotfix info says should be there.
  134. //
  135. LogHeader();
  136. if ( !ListNonMatchingHotfixes()) {
  137. LogItem( STR_NO_HOTFIXES_FOUND, NULL );
  138. }
  139. LogItem( 0, "\r\n" );
  140. if ( DoLogging ) {
  141. TerminateLog();
  142. }
  143. return;
  144. }
  145. DWORD GetCSDVersion(VOID)
  146. /*++
  147. Routine Description:
  148. Get the CSD Version number (to compare Service Pack number with)
  149. Return Value:
  150. The CSD Version number from the registry.
  151. --*/
  152. {
  153. NTSTATUS status;
  154. HKEY hCSDKey;
  155. DWORD cbValue;
  156. DWORD dwType;
  157. DWORD dwLocalCSDVersion;
  158. TCHAR szWindows[] = TEXT("SYSTEM\\CurrentControlSet\\Control\\Windows");
  159. TCHAR szCSD[] = TEXT("CSDVersion");
  160. status = RegOpenKeyEx(
  161. HKEY_LOCAL_MACHINE,
  162. szWindows,
  163. 0,
  164. KEY_READ,
  165. &hCSDKey
  166. );
  167. if (status != ERROR_SUCCESS){
  168. return(0);
  169. }
  170. cbValue = sizeof(DWORD);
  171. status = RegQueryValueEx(
  172. hCSDKey,
  173. szCSD,
  174. NULL, // Reserved
  175. &dwType,
  176. (PVOID)&dwLocalCSDVersion,
  177. &cbValue // size in bytes returned
  178. );
  179. RegCloseKey(hCSDKey);
  180. if ((status != ERROR_SUCCESS) ||
  181. (dwType != REG_DWORD)) {
  182. return(0);
  183. }
  184. return (dwLocalCSDVersion);
  185. }
  186. VOID
  187. LogHeader(
  188. VOID
  189. )
  190. {
  191. CHAR szInstallDate[MAX_PATH];
  192. LARGE_INTEGER CurrentTime;
  193. LARGE_INTEGER LocalTime;
  194. TIME_FIELDS TimeFields;
  195. //
  196. // Log the product name. Prior check for < W2K prevents
  197. // need for additional checks here.
  198. //
  199. if (( osvi.dwMajorVersion == 5 ) &&
  200. ( osvi.dwMinorVersion == 0 )) {
  201. LogItem( STR_VALIDATION_REPORT_W2K , NULL );
  202. } else if (( osvi.dwMajorVersion == 5 ) &&
  203. ( osvi.dwMinorVersion == 1 )) {
  204. LogItem( STR_VALIDATION_REPORT_XP, NULL );
  205. }
  206. LogItem( 0, ComputerName );
  207. //
  208. // Get the current system time
  209. //
  210. NtQuerySystemTime ( &CurrentTime );
  211. RtlSystemTimeToLocalTime( &CurrentTime, &LocalTime );
  212. RtlTimeToTimeFields( &LocalTime, &TimeFields );
  213. sprintf( szInstallDate,
  214. "%d/%d/%d %d:%02d%s",
  215. TimeFields.Month,
  216. TimeFields.Day,
  217. TimeFields.Year,
  218. (TimeFields.Hour % 12 == 0 ) ? 12 : TimeFields.Hour % 12,
  219. TimeFields.Minute,
  220. TimeFields.Hour >= 12 ? "pm" : "am" );
  221. LogItem( STR_REPORT_DATE, NULL );
  222. LogItem( 0, szInstallDate );
  223. LogItem( STR_SP_LEVEL, NULL );
  224. if ( GetCSDVersion() != 0 ) {
  225. LogItem( 0, osvi.szCSDVersion );
  226. } else {
  227. LogItem( STR_NO_SP_INSTALLED, NULL );
  228. }
  229. LogItem( STR_HOTFIXES_ID, NULL );
  230. }
  231. LPSTR
  232. CombinePaths(
  233. IN LPCSTR ParentPath,
  234. IN LPCSTR ChildPath,
  235. OUT LPSTR TargetPath // can be same as ParentPath if want to append
  236. )
  237. {
  238. ULONG ParentLength = strlen( ParentPath );
  239. LPSTR p;
  240. if ( ParentPath != TargetPath ) {
  241. memcpy( TargetPath, ParentPath, ParentLength );
  242. }
  243. p = TargetPath + ParentLength;
  244. if (( ParentLength > 0 ) &&
  245. ( *( p - 1 ) != '\\' ) &&
  246. ( *( p - 1 ) != '/' )) {
  247. *p++ = '\\';
  248. }
  249. strcpy( p, (ChildPath[0] != '\\') ? ChildPath : ChildPath+1 );
  250. return TargetPath;
  251. }
  252. BOOL
  253. ListNonMatchingHotfixes(
  254. VOID
  255. )
  256. /*++
  257. Routine Description:
  258. Check out each of the files listed under each SP and hotfix, and
  259. 1) Check that the files present on the system have correct version info and
  260. 2) Check that the files present on the system have valid signatures in the
  261. installed catalogs.
  262. Arguments:
  263. None
  264. Return Value:
  265. FALSE if no hotfixes were found on the system.
  266. --*/
  267. {
  268. char psz[MAX_PATH];
  269. DWORD cch = MAX_PATH;
  270. FILETIME ft;
  271. NTSTATUS status;
  272. DWORD i;
  273. DWORD j;
  274. DWORD h;
  275. DWORD cbValue;
  276. DWORD dwType;
  277. char lpFileName[MAX_PATH];
  278. char lpFileLocation[MAX_PATH];
  279. char lpFileVersion[MAX_PATH];
  280. char szRegW2K[MAX_PATH];
  281. char szRegSP[MAX_PATH];
  282. char szRegHotfix[MAX_PATH];
  283. char szFilelist[MAX_PATH];
  284. char szHotfixNumber[MAX_PATH];
  285. char szSPNumber[MAX_PATH];
  286. HKEY hW2KMainKey = 0;
  287. HKEY hHotfixMainKey = 0;
  288. HKEY hHotfixKey = 0;
  289. HKEY hFilelistKey = 0;
  290. DWORDLONG FileVersion;
  291. DWORDLONG TargetVersion;
  292. CHAR MiscBuffer1[ MISC_BUF_SIZE ];
  293. CHAR LogBuffer[ MISC_BUF_SIZE ];
  294. BOOL bIsHotfixWhacked;
  295. BOOL bIsSigInvalid;
  296. BOOL bAnyHotfixesInstalled = FALSE;
  297. LPWSTR FileName;
  298. LPWSTR FileLocation;
  299. HCATADMIN hCat = NULL;
  300. HANDLE hFileHandle = NULL;
  301. HANDLE hSfcServer = NULL;
  302. HINSTANCE hLibSfc = NULL;
  303. HMODULE hModuleWinTrust = NULL;
  304. HMODULE hModuleMsCat = NULL;
  305. HMODULE hModuleSetupApi = NULL;
  306. ISFILEPROTECTED IsFileProtected;
  307. CONNECTTOSFCSERVER ConnectToSfcServer;
  308. if (( osvi.dwMajorVersion == 5 ) &&
  309. ( osvi.dwMinorVersion == 0 )) {
  310. strcpy( szRegW2K, "SOFTWARE\\Microsoft\\Updates\\Windows 2000\\" );
  311. } else if (( osvi.dwMajorVersion == 5 ) &&
  312. ( osvi.dwMinorVersion == 1 )) {
  313. strcpy( szRegW2K, "SOFTWARE\\Microsoft\\Updates\\Windows XP\\" );
  314. }
  315. if (RegOpenKeyEx(
  316. HKEY_LOCAL_MACHINE,
  317. szRegW2K,
  318. 0,
  319. KEY_READ,
  320. &hW2KMainKey
  321. ) != ERROR_SUCCESS) {
  322. goto BailOut;
  323. }
  324. //
  325. // Get function names for all the crypto routines that we're going to call
  326. //
  327. hModuleWinTrust = LoadLibrary( "wintrust.dll" );
  328. if ( hModuleWinTrust == NULL ) {
  329. goto BailOut;
  330. }
  331. hModuleMsCat = LoadLibrary( "mscat32.dll" );
  332. if ( hModuleMsCat == NULL ) {
  333. goto BailOut;
  334. }
  335. hModuleSetupApi = LoadLibrary("setupapi.dll");
  336. if(hModuleSetupApi == NULL) {
  337. goto BailOut;
  338. }
  339. pWinVerifyTrust = (PWINVERIFYTRUST)GetProcAddress( hModuleWinTrust, "WinVerifyTrust" );
  340. pCryptCATAdminAcquireContext = (PCRYPTCATADMINACQUIRECONTEXT)GetProcAddress( hModuleMsCat, "CryptCATAdminAcquireContext" );
  341. pCryptCATAdminReleaseContext = (PCRYPTCATADMINRELEASECONTEXT)GetProcAddress( hModuleMsCat, "CryptCATAdminReleaseContext" );
  342. pCryptCATAdminCalcHashFromFileHandle = (PCRYPTCATADMINCALCHASHFROMFILEHANDLE)GetProcAddress( hModuleMsCat, "CryptCATAdminCalcHashFromFileHandle" );
  343. pCryptCATAdminEnumCatalogFromHash = (PCRYPTCATADMINENUMCATALOGFROMHASH)GetProcAddress( hModuleMsCat, "CryptCATAdminEnumCatalogFromHash" );
  344. pCryptCATCatalogInfoFromContext = (PCRYPTCATCATALOGINFOFROMCONTEXT)GetProcAddress( hModuleMsCat, "CryptCATCatalogInfoFromContext" );
  345. pCryptCATAdminReleaseCatalogContext = (PCRYPTCATADMINRELEASECATALOGCONTEXT)GetProcAddress( hModuleMsCat, "CryptCATAdminReleaseCatalogContext" );
  346. // Attempt to get Win2k version
  347. pMultiByteToUnicode = (PMULTIBYTETOUNICODE)GetProcAddress(hModuleSetupApi, "MultiByteToUnicode");
  348. // Attempt to get Whistler version
  349. if(pMultiByteToUnicode == NULL) {
  350. pMultiByteToUnicode = (PMULTIBYTETOUNICODE)GetProcAddress(hModuleSetupApi, "pSetupMultiByteToUnicode");
  351. }
  352. // Fail
  353. if(pMultiByteToUnicode == NULL) {
  354. goto BailOut;
  355. }
  356. pCryptCATAdminAcquireContext( &hCat, &DriverVerifyGuid, 0 );
  357. //
  358. // Get the addresses for the SFC function calls
  359. //
  360. if ( (hLibSfc = LoadLibrary("SFC.DLL")) != NULL ) {
  361. ConnectToSfcServer = (CONNECTTOSFCSERVER)GetProcAddress( hLibSfc, (LPCSTR)0x00000003 );
  362. if ( ConnectToSfcServer == NULL ) {
  363. goto BailOut;
  364. }
  365. hSfcServer = ConnectToSfcServer( NULL );
  366. if ( hSfcServer == NULL ) {
  367. goto BailOut;
  368. }
  369. IsFileProtected = (ISFILEPROTECTED)GetProcAddress( hLibSfc, "SfcIsFileProtected" );
  370. if ( IsFileProtected == NULL ) {
  371. goto BailOut;
  372. }
  373. } else {
  374. goto BailOut;
  375. }
  376. //
  377. // Now cycle through all the hotfixes stored in the registry
  378. //
  379. h = 0;
  380. while ( RegEnumKeyEx( hW2KMainKey,
  381. h,
  382. szSPNumber,
  383. &cch,
  384. NULL,
  385. NULL,
  386. NULL,
  387. &ft ) == ERROR_SUCCESS ) {
  388. strcpy( szRegSP, szRegW2K );
  389. strcat( szRegSP, szSPNumber );
  390. strcat( szRegSP, "\\" );
  391. if (RegOpenKeyEx(
  392. HKEY_LOCAL_MACHINE,
  393. szRegSP,
  394. 0,
  395. KEY_READ,
  396. &hHotfixMainKey
  397. ) != ERROR_SUCCESS) {
  398. goto BailOut;
  399. }
  400. i = 0;
  401. cch = MAX_PATH;
  402. while ( RegEnumKeyEx( hHotfixMainKey,
  403. i,
  404. szHotfixNumber,
  405. &cch,
  406. NULL,
  407. NULL,
  408. NULL,
  409. &ft ) == ERROR_SUCCESS ) {
  410. strcpy( szRegHotfix, szRegSP );
  411. strcat( szRegHotfix, szHotfixNumber );
  412. strcat( szRegHotfix, "\\Filelist\\" );
  413. strcpy( LogBuffer, szHotfixNumber );
  414. strcat( LogBuffer, ": " );
  415. LogItem( 0, "\r\n" );
  416. LogItem( 0, LogBuffer );
  417. bIsHotfixWhacked = FALSE;
  418. bIsSigInvalid = FALSE;
  419. bAnyHotfixesInstalled = TRUE;
  420. if ( RegOpenKeyEx(
  421. HKEY_LOCAL_MACHINE,
  422. szRegHotfix,
  423. 0,
  424. KEY_READ,
  425. &hHotfixKey
  426. ) == ERROR_SUCCESS) {
  427. j = 0;
  428. cch = MAX_PATH;
  429. while ( RegEnumKeyEx( hHotfixKey,
  430. j,
  431. psz,
  432. &cch,
  433. NULL,
  434. NULL,
  435. NULL,
  436. &ft ) == ERROR_SUCCESS ) {
  437. //
  438. // At this point, szFilelist is something like:
  439. // HKLM\SOFTWARE\Microsoft\Updates\Windows 2000\SP2\Q123456\Filelist
  440. //
  441. strcpy( szFilelist, szRegHotfix );
  442. strcat( szFilelist, psz );
  443. if ( RegOpenKeyEx(
  444. HKEY_LOCAL_MACHINE,
  445. szFilelist,
  446. 0,
  447. KEY_READ,
  448. &hFilelistKey
  449. ) == ERROR_SUCCESS) {
  450. //
  451. // Get all the values out of the registry we care about:
  452. // File location, name, and version string.
  453. //
  454. cbValue = MAX_PATH;
  455. status = RegQueryValueEx(
  456. hFilelistKey,
  457. "FileName",
  458. NULL, // Reserved
  459. &dwType,
  460. lpFileName, // Buffer
  461. &cbValue // size in bytes returned
  462. );
  463. if ( status != ERROR_SUCCESS ) {
  464. goto BailOut;
  465. }
  466. cbValue = MAX_PATH;
  467. status = RegQueryValueEx(
  468. hFilelistKey,
  469. "Location",
  470. NULL, // Reserved
  471. &dwType,
  472. lpFileLocation, // Buffer
  473. &cbValue // size in bytes returned
  474. );
  475. if ( status != ERROR_SUCCESS ) {
  476. goto BailOut;
  477. }
  478. cbValue = MAX_PATH;
  479. status = RegQueryValueEx(
  480. hFilelistKey,
  481. "Version",
  482. NULL, // Reserved
  483. &dwType,
  484. lpFileVersion, // Buffer
  485. &cbValue // size in bytes returned
  486. );
  487. if ( status != ERROR_SUCCESS ) {
  488. continue;
  489. }
  490. //
  491. // Now see if the file in question got whacked by SFC
  492. //
  493. if ( ConvertVersionStringToQuad( lpFileVersion, &FileVersion )) {
  494. CombinePaths( lpFileLocation, lpFileName, lpFileLocation );
  495. if (( MyGetFileVersion( lpFileLocation, &TargetVersion )) &&
  496. ( TargetVersion < FileVersion )) {
  497. if ( !bIsHotfixWhacked ) {
  498. LogItem( STR_REINSTALL_HOTFIX, NULL );
  499. if ( Verbose ) {
  500. LogItem( STR_FILES_MISSING, NULL );
  501. }
  502. }
  503. if ( Verbose ) {
  504. LogItem( 0, "\t\t" );
  505. LogItem( 0, _strupr( lpFileLocation ));
  506. LogItem( 0, "\r\n" );
  507. }
  508. bIsHotfixWhacked = TRUE;
  509. }
  510. }
  511. }
  512. j++;
  513. cch = MAX_PATH;
  514. }
  515. //
  516. // Now do it again, but this time check each files' hash
  517. // against the installed catalog files.
  518. //
  519. j = 0;
  520. cch = MAX_PATH;
  521. while ( RegEnumKeyEx( hHotfixKey,
  522. j,
  523. psz,
  524. &cch,
  525. NULL,
  526. NULL,
  527. NULL,
  528. &ft ) == ERROR_SUCCESS ) {
  529. //
  530. // At this point, szFilelist is something like:
  531. // HKLM\SOFTWARE\Microsoft\Updates\Windows 2000\SP2\Q123456\Filelist
  532. //
  533. strcpy( szFilelist, szRegHotfix );
  534. strcat( szFilelist, psz );
  535. if ( RegOpenKeyEx(
  536. HKEY_LOCAL_MACHINE,
  537. szFilelist,
  538. 0,
  539. KEY_READ,
  540. &hFilelistKey
  541. ) == ERROR_SUCCESS) {
  542. //
  543. // Get all the values out of the registry we care about:
  544. // File location, name, and version string.
  545. //
  546. cbValue = MAX_PATH;
  547. status = RegQueryValueEx(
  548. hFilelistKey,
  549. "FileName",
  550. NULL, // Reserved
  551. &dwType,
  552. lpFileName, // Buffer
  553. &cbValue // size in bytes returned
  554. );
  555. if ( status != ERROR_SUCCESS ) {
  556. goto BailOut;
  557. }
  558. cbValue = MAX_PATH;
  559. status = RegQueryValueEx(
  560. hFilelistKey,
  561. "Location",
  562. NULL, // Reserved
  563. &dwType,
  564. lpFileLocation, // Buffer
  565. &cbValue // size in bytes returned
  566. );
  567. if ( status != ERROR_SUCCESS ) {
  568. goto BailOut;
  569. }
  570. //
  571. // Now see if the file in question has a hash
  572. // in an installed cat file.
  573. //
  574. CombinePaths( lpFileLocation, lpFileName, lpFileLocation );
  575. FileName = pMultiByteToUnicode( (LPSTR)lpFileName, CP_ACP );
  576. FileLocation = pMultiByteToUnicode( (LPSTR)lpFileLocation, CP_ACP );
  577. hFileHandle = CreateFile( lpFileLocation,
  578. GENERIC_READ,
  579. FILE_SHARE_READ,
  580. NULL,
  581. OPEN_EXISTING,
  582. 0,
  583. NULL
  584. );
  585. if ( hFileHandle == INVALID_HANDLE_VALUE ) {
  586. continue;
  587. }
  588. if ( IsFileProtected( hSfcServer, FileLocation )) {
  589. if ( !ValidateFileSignature(
  590. hCat,
  591. hFileHandle,
  592. FileName,
  593. FileLocation
  594. )) {
  595. if ( !bIsHotfixWhacked && !bIsSigInvalid ) {
  596. LogItem( STR_REINSTALL_HOTFIX, NULL );
  597. }
  598. if ( Verbose && !bIsSigInvalid ) {
  599. LogItem( STR_NO_MATCHING_SIG, NULL );
  600. }
  601. if ( Verbose ) {
  602. LogItem( 0, "\t\t" );
  603. LogItem( 0, _strupr( lpFileLocation ));
  604. LogItem( 0, "\r\n" );
  605. }
  606. bIsSigInvalid = TRUE;
  607. }
  608. }
  609. if ( hFileHandle ) {
  610. CloseHandle( hFileHandle );
  611. }
  612. }
  613. j++;
  614. cch = MAX_PATH;
  615. }
  616. }
  617. i++;
  618. cch = MAX_PATH;
  619. if ( !bIsHotfixWhacked && !bIsSigInvalid ) {
  620. LogItem( STR_HOTFIX_CURRENT, NULL );
  621. }
  622. }
  623. h++;
  624. cch = MAX_PATH;
  625. }
  626. BailOut:
  627. if ( hHotfixMainKey ) {
  628. CloseHandle( hHotfixMainKey );
  629. }
  630. if ( hHotfixKey ) {
  631. CloseHandle( hHotfixKey );
  632. }
  633. if ( hFilelistKey ) {
  634. CloseHandle( hFilelistKey );
  635. }
  636. if ( hSfcServer ) {
  637. CLOSESFC CloseSfc = (CLOSESFC)GetProcAddress( hLibSfc, (LPCSTR)0x00000004 );
  638. if ( CloseSfc != NULL ) {
  639. CloseSfc( hSfcServer );
  640. }
  641. }
  642. if ( hCat ) {
  643. pCryptCATAdminReleaseContext( hCat, 0 );
  644. }
  645. if ( hLibSfc ) {
  646. FreeLibrary( hLibSfc );
  647. }
  648. if ( hModuleWinTrust ) {
  649. FreeLibrary( hModuleWinTrust );
  650. }
  651. if ( hModuleMsCat ) {
  652. FreeLibrary( hModuleMsCat );
  653. }
  654. if(hModuleSetupApi) {
  655. FreeLibrary(hModuleSetupApi);
  656. }
  657. return bAnyHotfixesInstalled;
  658. }
  659. BOOL
  660. MyGetFileVersion(
  661. IN LPCSTR FileName,
  662. OUT DWORDLONG *Version
  663. )
  664. {
  665. BOOL Success = FALSE;
  666. DWORD Blah;
  667. DWORD Size;
  668. Size = GetFileVersionInfoSize( (LPSTR)FileName, &Blah );
  669. if ( Size ) {
  670. PVOID Buffer = malloc( Size );
  671. if ( Buffer ) {
  672. if ( GetFileVersionInfo( (LPSTR)FileName, 0, Size, Buffer )) {
  673. VS_FIXEDFILEINFO *VersionInfo;
  674. if ( VerQueryValue( Buffer, "\\", &VersionInfo, &Blah )) {
  675. *Version = (DWORDLONG)( VersionInfo->dwFileVersionMS ) << 32
  676. | (DWORDLONG)( VersionInfo->dwFileVersionLS );
  677. Success = TRUE;
  678. }
  679. }
  680. free( Buffer );
  681. }
  682. }
  683. return Success;
  684. }
  685. BOOL
  686. ConvertVersionStringToQuad(
  687. IN LPCSTR lpFileVersion,
  688. OUT DWORDLONG *FileVersion
  689. )
  690. {
  691. WORD FileverMSUW = 0;
  692. WORD FileverMSLW = 0;
  693. WORD FileverLSUW = 0;
  694. WORD FileverLSLW = 0;
  695. CHAR *p, *q, *r, *s, *t, *u, *w;
  696. p = strchr( lpFileVersion, '.' );
  697. if ( p ) {
  698. q = p + 1;
  699. *p = 0;
  700. FileverMSUW = (WORD)atoi( lpFileVersion );
  701. r = strchr( q, '.' );
  702. if ( r ) {
  703. s = r + 1;
  704. *r = 0;
  705. FileverMSLW = (WORD)atoi( q );
  706. t = strchr( s, '.' );
  707. if ( t ) {
  708. u = t + 1;
  709. *t = 0;
  710. FileverLSUW = (WORD)atoi( s );
  711. FileverLSLW = (WORD)atoi( u );
  712. } else {
  713. return FALSE;
  714. }
  715. } else {
  716. return FALSE;
  717. }
  718. } else {
  719. return FALSE;
  720. }
  721. *FileVersion = (DWORDLONG)( FileverMSUW ) << 48
  722. | (DWORDLONG)( FileverMSLW ) << 32
  723. | (DWORDLONG)( FileverLSUW ) << 16
  724. | (DWORDLONG)( FileverLSLW );
  725. return TRUE;
  726. }
  727. BOOL
  728. InitializeLog(
  729. BOOL WipeLogFile,
  730. LPCSTR NameOfLogFile
  731. )
  732. {
  733. //
  734. // If we're wiping the logfile clean, attempt to delete
  735. // what's there.
  736. //
  737. if ( WipeLogFile ) {
  738. SetFileAttributes( NameOfLogFile, FILE_ATTRIBUTE_NORMAL );
  739. DeleteFile( NameOfLogFile );
  740. }
  741. //
  742. // Open/create the file.
  743. //
  744. LogFile = CreateFile(
  745. NameOfLogFile,
  746. GENERIC_READ | GENERIC_WRITE,
  747. FILE_SHARE_READ | FILE_SHARE_WRITE,
  748. NULL,
  749. OPEN_ALWAYS,
  750. FILE_ATTRIBUTE_NORMAL,
  751. NULL
  752. );
  753. if ( LogFile == INVALID_HANDLE_VALUE ) {
  754. LogFile = NULL;
  755. }
  756. return( LogFile != NULL );
  757. }
  758. VOID
  759. TerminateLog(
  760. VOID
  761. )
  762. {
  763. if( LogFile ) {
  764. CloseHandle( LogFile );
  765. LogFile = NULL;
  766. }
  767. }
  768. BOOL
  769. LogItem(
  770. IN DWORD Description,
  771. IN LPCSTR LogString
  772. )
  773. {
  774. BOOL b = FALSE;
  775. DWORD BytesWritten;
  776. CHAR TextBuffer[ MISC_BUF_SIZE ];
  777. if ( !Description ) {
  778. //
  779. // Description of 0 means use the passed in LogString instead
  780. //
  781. strcpy( TextBuffer, LogString );
  782. } else {
  783. //
  784. // Get the string from the resource
  785. //
  786. LoadString( NULL, Description, TextBuffer, sizeof(TextBuffer) );
  787. }
  788. PrintStringToConsole( TextBuffer );
  789. if ( LogFile ) {
  790. //
  791. // Make sure we write to current end of file.
  792. //
  793. SetFilePointer( LogFile, 0, NULL, FILE_END );
  794. //
  795. // Write the text.
  796. //
  797. b = WriteFile(
  798. LogFile,
  799. TextBuffer,
  800. strlen(TextBuffer),
  801. &BytesWritten,
  802. NULL
  803. );
  804. } else {
  805. //
  806. // No logging, just return success
  807. //
  808. b = TRUE;
  809. }
  810. return( b );
  811. }
  812. void
  813. MyLowerString(
  814. IN PWSTR String,
  815. IN ULONG StringLength // in characters
  816. )
  817. {
  818. ULONG i;
  819. for (i=0; i<StringLength; i++) {
  820. String[i] = towlower(String[i]);
  821. }
  822. }
  823. BOOL
  824. ValidateFileSignature(
  825. IN HCATADMIN hCatAdmin,
  826. IN HANDLE RealFileHandle,
  827. IN PCWSTR BaseFileName,
  828. IN PCWSTR CompleteFileName
  829. )
  830. /*++
  831. Routine Description:
  832. Checks if the signature for a given file is valid using WinVerifyTrust
  833. Arguments:
  834. hCatAdmin - admin context handle for checking file signature
  835. RealFileHandle - file handle to the file to be verified
  836. BaseFileName - filename without the path of the file to be verified
  837. CompleteFileName - fully qualified filename with path
  838. Return Value:
  839. TRUE if the file has a valid signature.
  840. --*/
  841. {
  842. BOOL rVal = FALSE;
  843. DWORD HashSize;
  844. LPBYTE Hash = NULL;
  845. ULONG SigErr = ERROR_SUCCESS;
  846. WINTRUST_DATA WintrustData;
  847. WINTRUST_CATALOG_INFO WintrustCatalogInfo;
  848. WINTRUST_FILE_INFO WintrustFileInfo;
  849. WCHAR UnicodeKey[MAX_PATH];
  850. HCATINFO PrevCat;
  851. HCATINFO hCatInfo;
  852. CATALOG_INFO CatInfo;
  853. //
  854. // initialize some of the structure that we will pass into winverifytrust.
  855. // we don't know if we're checking against a catalog or directly against a
  856. // file at this point
  857. //
  858. ZeroMemory(&WintrustData, sizeof(WINTRUST_DATA));
  859. WintrustData.cbStruct = sizeof(WINTRUST_DATA);
  860. WintrustData.dwUIChoice = WTD_UI_NONE;
  861. WintrustData.fdwRevocationChecks = WTD_REVOKE_NONE;
  862. WintrustData.dwStateAction = WTD_STATEACTION_IGNORE;
  863. WintrustData.pCatalog = &WintrustCatalogInfo;
  864. WintrustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE;
  865. Hash = NULL;
  866. //
  867. // we first calculate a hash for our file. start with a reasonable
  868. // hash size and grow larger as needed
  869. //
  870. HashSize = 100;
  871. do {
  872. Hash = LocalAlloc( LPTR, HashSize );
  873. if(!Hash) {
  874. SigErr = ERROR_NOT_ENOUGH_MEMORY;
  875. break;
  876. }
  877. if(pCryptCATAdminCalcHashFromFileHandle(RealFileHandle,
  878. &HashSize,
  879. Hash,
  880. 0)) {
  881. SigErr = ERROR_SUCCESS;
  882. } else {
  883. SigErr = GetLastError();
  884. //
  885. // If this API did screw up and not set last error, go ahead
  886. // and set something.
  887. //
  888. if(SigErr == ERROR_SUCCESS) {
  889. SigErr = ERROR_INVALID_DATA;
  890. }
  891. LocalFree( Hash );
  892. Hash = NULL; // reset this so we won't try to free it later
  893. if(SigErr != ERROR_INSUFFICIENT_BUFFER) {
  894. //
  895. // The API failed for some reason other than
  896. // buffer-too-small. We gotta bail.
  897. //
  898. break;
  899. }
  900. }
  901. } while (SigErr != ERROR_SUCCESS);
  902. if (SigErr != ERROR_SUCCESS) {
  903. //
  904. // if we failed at this point there are a few reasons:
  905. //
  906. //
  907. // 1) a bug in this code
  908. // 2) we are in a low memory situation
  909. // 3) the file's hash cannot be calculated on purpose (in the case
  910. // of a catalog file, a hash cannot be calculated because a catalog
  911. // cannot sign another catalog. In this case, we check to see if
  912. // the file is "self-signed".
  913. hCatInfo = NULL;
  914. goto selfsign;
  915. }
  916. //
  917. // Now we have the file's hash. Initialize the structures that
  918. // will be used later on in calls to WinVerifyTrust.
  919. //
  920. WintrustData.dwUnionChoice = WTD_CHOICE_CATALOG;
  921. ZeroMemory(&WintrustCatalogInfo, sizeof(WINTRUST_CATALOG_INFO));
  922. WintrustCatalogInfo.cbStruct = sizeof(WINTRUST_CATALOG_INFO);
  923. WintrustCatalogInfo.pbCalculatedFileHash = Hash;
  924. WintrustCatalogInfo.cbCalculatedFileHash = HashSize;
  925. //
  926. // WinVerifyTrust is case-sensitive, so ensure that the key
  927. // being used is all lower-case!
  928. //
  929. // Copy the key to a writable Unicode character buffer so we
  930. // can lower-case it.
  931. //
  932. wcsncpy(UnicodeKey, BaseFileName, (sizeof(UnicodeKey)/sizeof(WCHAR)));
  933. MyLowerString(UnicodeKey, wcslen(UnicodeKey));
  934. WintrustCatalogInfo.pcwszMemberTag = UnicodeKey;
  935. //
  936. // Search through installed catalogs looking for those that
  937. // contain data for a file with the hash we just calculated.
  938. //
  939. PrevCat = NULL;
  940. hCatInfo = pCryptCATAdminEnumCatalogFromHash(
  941. hCatAdmin,
  942. Hash,
  943. HashSize,
  944. 0,
  945. &PrevCat
  946. );
  947. if (hCatInfo == NULL) {
  948. SigErr = GetLastError();
  949. }
  950. while(hCatInfo) {
  951. CatInfo.cbStruct = sizeof(CATALOG_INFO);
  952. if (pCryptCATCatalogInfoFromContext(hCatInfo, &CatInfo, 0)) {
  953. //
  954. // Attempt to validate against each catalog we
  955. // enumerate. Note that the catalog file info we
  956. // get back gives us a fully qualified path.
  957. //
  958. WintrustData.dwStateAction = WTD_STATEACTION_AUTO_CACHE_FLUSH;
  959. WintrustCatalogInfo.pcwszCatalogFilePath = CatInfo.wszCatalogFile;
  960. SigErr = (DWORD)pWinVerifyTrust(
  961. NULL,
  962. &DriverVerifyGuid,
  963. &WintrustData
  964. );
  965. WintrustData.dwStateAction = WTD_STATEACTION_IGNORE;
  966. //
  967. // NOTE: Because we're using cached
  968. // catalog information (i.e., the
  969. // WTD_STATEACTION_AUTO_CACHE flag), we
  970. // don't need to explicitly validate the
  971. // catalog itself first.
  972. //
  973. WintrustCatalogInfo.pcwszCatalogFilePath = CatInfo.wszCatalogFile;
  974. SigErr = (DWORD)pWinVerifyTrust(
  975. NULL,
  976. &DriverVerifyGuid,
  977. &WintrustData
  978. );
  979. //
  980. // If the result of the above validations is
  981. // success, then we're done.
  982. //
  983. if(SigErr == ERROR_SUCCESS) {
  984. //
  985. // BugBug: wierd API :
  986. // in the success case, we must release the catalog info handle
  987. // in the failure case, we implicitly free PrevCat
  988. // if we explicitly free the catalog, we will double free the
  989. // handle!!!
  990. //
  991. pCryptCATAdminReleaseCatalogContext(hCatAdmin,hCatInfo,0);
  992. break;
  993. }
  994. }
  995. PrevCat = hCatInfo;
  996. hCatInfo = pCryptCATAdminEnumCatalogFromHash(hCatAdmin, Hash, HashSize, 0, &PrevCat);
  997. }
  998. selfsign:
  999. if (hCatInfo == NULL) {
  1000. //
  1001. // We exhausted all the applicable catalogs without
  1002. // finding the one we needed.
  1003. //
  1004. SigErr = GetLastError();
  1005. //
  1006. // Make sure we have a valid error code.
  1007. //
  1008. if(SigErr == ERROR_SUCCESS) {
  1009. SigErr = ERROR_INVALID_DATA;
  1010. }
  1011. //
  1012. // The file failed to validate using the specified
  1013. // catalog. See if the file validates without a
  1014. // catalog (i.e., the file contains its own
  1015. // signature).
  1016. //
  1017. WintrustData.dwUnionChoice = WTD_CHOICE_FILE;
  1018. WintrustData.pFile = &WintrustFileInfo;
  1019. ZeroMemory(&WintrustFileInfo, sizeof(WINTRUST_FILE_INFO));
  1020. WintrustFileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO);
  1021. WintrustFileInfo.pcwszFilePath = CompleteFileName;
  1022. WintrustFileInfo.hFile = RealFileHandle;
  1023. SigErr = (DWORD)pWinVerifyTrust(
  1024. NULL,
  1025. &DriverVerifyGuid,
  1026. &WintrustData
  1027. );
  1028. if(SigErr != ERROR_SUCCESS) {
  1029. //
  1030. // in this case the file is not in any of our catalogs
  1031. // and it does not contain its own signature
  1032. //
  1033. }
  1034. }
  1035. if(SigErr == ERROR_SUCCESS) {
  1036. rVal = TRUE;
  1037. }
  1038. if (Hash) {
  1039. LocalFree( Hash );
  1040. }
  1041. return rVal;
  1042. }
  1043. VOID
  1044. PrintStringToConsole(
  1045. IN LPCSTR StringToPrint
  1046. )
  1047. {
  1048. if ( !QuietMode ) {
  1049. printf( StringToPrint );
  1050. }
  1051. }
  1052. BOOL
  1053. ParseArgs(
  1054. IN int argc,
  1055. IN char **argv
  1056. )
  1057. {
  1058. int i = 1;
  1059. int j;
  1060. while( i < argc ) {
  1061. switch (*argv[i]) {
  1062. case '/' :
  1063. case '-' :
  1064. ++argv[i];
  1065. switch( *argv[i] ) {
  1066. case 'v':
  1067. case 'V':
  1068. Verbose = TRUE;
  1069. break;
  1070. case 'q':
  1071. case 'Q':
  1072. QuietMode = TRUE;
  1073. break;
  1074. case 'l':
  1075. case 'L':
  1076. DoLogging = TRUE;
  1077. ++argv[i];
  1078. if ( *argv[i] == ':' ) {
  1079. ++argv[i];
  1080. } else {
  1081. break;
  1082. }
  1083. if ( *argv[i] ) {
  1084. strcpy( CmdLineLocation, argv[i]);
  1085. } else {
  1086. return FALSE;
  1087. }
  1088. break;
  1089. default:
  1090. return FALSE;
  1091. }
  1092. break;
  1093. default:
  1094. return FALSE;
  1095. }
  1096. i++;
  1097. }
  1098. return TRUE;
  1099. }