/* Copyright (c) Microsoft Corporation */ #include "stdinc.h" #include "windows.h" #include "sxsapi.h" #include "sxsprotect.h" #include "sxssfcscan.h" #include "wintrust.h" #include "softpub.h" #include "strongname.h" #include "recover.h" BOOL SxspValidateEntireAssembly( DWORD dwFlags, const CAssemblyRecoveryInfo &RecoverInfo, DWORD &dwResult, PCASSEMBLY_IDENTITY pAssemblyIdentity, const CBaseStringBuffer *pbuffWinsxsRoot ) { BOOL bSuccess = FALSE; const CSecurityMetaData &rSecurityData = RecoverInfo.GetSecurityInformation(); FN_TRACE_WIN32(bSuccess); #define CHECKSHOULDSTOPFAIL { if (dwFlags & SXS_VALIDATE_ASM_FLAG_MODE_STOP_ON_FAIL) { bSuccess = TRUE; goto Exit; } } #define ADDFLAG(result, test, flag) { if (test) { (result) |= (flag); } else CHECKSHOULDSTOPFAIL } ManifestValidationResult ManifestValidity; CSecurityMetaData SecurityMetaData; CStringBuffer sbManifestPath; dwResult = 0; if (dwFlags == 0) { dwFlags = SXS_VALIDATE_ASM_FLAG_CHECK_EVERYTHING; dwFlags |= (SXS_VALIDATE_ASM_FLAG_MODE_STOP_ON_FAIL); } IFINVALID_FLAGS_EXIT_WIN32(dwFlags, SXS_VALIDATE_ASM_FLAG_CHECK_CATALOG | SXS_VALIDATE_ASM_FLAG_CHECK_FILES | SXS_VALIDATE_ASM_FLAG_CHECK_STRONGNAME | SXS_VALIDATE_ASM_FLAG_CHECK_CAT_STRONGNAME | SXS_VALIDATE_ASM_FLAG_MODE_STOP_ON_FAIL); // // Asking us to check the catalog when there's no catalog on the assembly // is a Bad Thing. Perhaps this should just return TRUE with a missing catalog? // PARAMETER_CHECK(dwFlags & SXS_VALIDATE_ASM_FLAG_CHECK_CATALOG); PARAMETER_CHECK(RecoverInfo.GetHasCatalog()); #if DBG ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS.DLL: %s() - Beginning protection scan of %ls, flags 0x%08x\n", __FUNCTION__, static_cast(RecoverInfo.GetAssemblyDirectoryName()), dwFlags); #endif if (pAssemblyIdentity == NULL) { IFW32FALSE_EXIT(::SxspResolveAssemblyManifestPath(RecoverInfo.GetAssemblyDirectoryName(), sbManifestPath)); } else { CTinyStringBuffer buffWinsxsRoot; PROBING_ATTRIBUTE_CACHE AttributeCache = { 0 }; if (pbuffWinsxsRoot == NULL) { IFW32FALSE_EXIT(::SxspGetAssemblyRootDirectory(buffWinsxsRoot)); pbuffWinsxsRoot = &buffWinsxsRoot; } IFW32FALSE_EXIT( ::SxspGenerateSxsPath_FullPathToManifestOrPolicyFile( *pbuffWinsxsRoot, pAssemblyIdentity, &AttributeCache, sbManifestPath)); } // // If we're checking the catalog, then do it. If the catalog is bad or // otherwise doesn't match the actual assembly, then we need to mark // ourselves as successful, then exit. // if (dwFlags & SXS_VALIDATE_ASM_FLAG_CHECK_CATALOG) { IFW32FALSE_EXIT(::SxspValidateManifestAgainstCatalog( sbManifestPath, ManifestValidity, 0)); #if DBG ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS.DLL: Manifest Validity = %ls\n", ::SxspManifestValidationResultToString(ManifestValidity)); #endif ADDFLAG( dwResult, ManifestValidity == ManifestValidate_IsIntact, SXS_VALIDATE_ASM_FLAG_VALID_CATALOG); } //TODO: Problems - make sure that we validate the manifest against what's in the // registry. Maybe we need a special mode of incorporating assemblies (over a manifest) // that will just fill out the security data and nothing else... // // Validate the strong name of the assembly first. // if (dwFlags & SXS_VALIDATE_ASM_FLAG_CHECK_STRONGNAME) { // // JW 3/19/2001 - Public keys are NO LONGER IN THE BUILD, and as such // this check is moot. // ADDFLAG(dwResult, TRUE, SXS_VALIDATE_ASM_FLAG_VALID_STRONGNAME); } // // Let's open the catalog and scour through it certificate-wise // looking for a strong name that matches up. // if (dwFlags & SXS_VALIDATE_ASM_FLAG_CHECK_CAT_STRONGNAME) { CStringBuffer sbCatalogName; CSmallStringBuffer sbTheorheticalStrongName; const CFusionByteArray &rbaSignerPublicKey = rSecurityData.GetSignerPublicKeyTokenBits(); CPublicKeyInformation PublicKeyInfo; BOOL bStrongNameFoundInCatalog; IFW32FALSE_EXIT(sbCatalogName.Win32Assign(sbManifestPath)); IFW32FALSE_EXIT(sbCatalogName.Win32ChangePathExtension( FILE_EXTENSION_CATALOG, FILE_EXTENSION_CATALOG_CCH, eAddIfNoExtension)); if (!PublicKeyInfo.Initialize(sbCatalogName)) { const DWORD dwLastError = ::FusionpGetLastWin32Error(); if ((dwLastError != ERROR_PATH_NOT_FOUND) && (dwLastError != ERROR_FILE_NOT_FOUND)) goto Exit; } IFW32FALSE_EXIT( ::SxspHashBytesToString( rbaSignerPublicKey.GetArrayPtr(), rbaSignerPublicKey.GetSize(), sbTheorheticalStrongName)); IFW32FALSE_EXIT( PublicKeyInfo.DoesStrongNameMatchSigner( sbTheorheticalStrongName, bStrongNameFoundInCatalog)); ADDFLAG(dwResult, bStrongNameFoundInCatalog, SXS_VALIDATE_ASM_FLAG_VALID_CAT_STRONGNAME); } // // Now, scan through all the files that are listed in the manifest and // ensure that they're all OK. // if (dwFlags & SXS_VALIDATE_ASM_FLAG_CHECK_FILES) { CStringBuffer sbTempScanPath; CStringBuffer sbAsmRootDir; const CBaseStringBuffer &sbAssemblyName = RecoverInfo.GetAssemblyDirectoryName(); CFileInformationTableIter ContentTableIter(const_cast(rSecurityData.GetFileDataTable())); HashValidateResult hvResult; BOOL bAllFilesMatch = TRUE; BOOL fTempBoolean; SIZE_T cchPath; IFW32FALSE_EXIT(::SxspGetAssemblyRootDirectory(sbAsmRootDir)); IFW32FALSE_EXIT(sbTempScanPath.Win32Assign(sbAsmRootDir)); IFW32FALSE_EXIT(sbTempScanPath.Win32AppendPathElement(sbAssemblyName)); cchPath = sbTempScanPath.Cch(); for (ContentTableIter.Reset(); ContentTableIter.More(); ContentTableIter.Next()) { // // Cobble together a path to scan for the file in, based on the // assembly root directory, the 'name' of the assembly (note: // we can't use this to go backwards to get an identity, // unfortunately), and the name of the file to be validated. // PCWSTR wsString = ContentTableIter.GetKey(); CMetaDataFileElement &HashEntry = ContentTableIter.GetValue(); sbTempScanPath.Left(cchPath); IFW32FALSE_EXIT(sbTempScanPath.Win32AppendPathElement(wsString, ::wcslen(wsString))); IFW32FALSE_EXIT_UNLESS( ::SxspValidateAllFileHashes( HashEntry, sbTempScanPath, hvResult ), FILE_OR_PATH_NOT_FOUND(::FusionpGetLastWin32Error()), fTempBoolean ); if ( ( hvResult != HashValidate_Matches ) || ( fTempBoolean ) ) { bAllFilesMatch = FALSE; break; } } ADDFLAG(dwResult, bAllFilesMatch, SXS_VALIDATE_ASM_FLAG_VALID_FILES); } // // Phew - should be done doing everything the user wanted us to. // bSuccess = TRUE; Exit: #if DBG ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS.DLL: Done validating, result = 0x%08x, success = %d\n", dwResult, bSuccess); #endif return bSuccess; #undef CHECKSHOULDSTOPFAIL } // // Single-shot scanning // BOOL SxsProtectionPerformScanNowNoSEH( HWND hwProgressWindow, BOOL bValidate, BOOL bUIAllowed ) { BOOL bSuccess = TRUE; FN_TRACE_WIN32(bSuccess); CFusionRegKey hkInstallRoot; CStringBuffer sbKeyName; DWORD dwKeyIndex; BOOL fNoMoreKeys = FALSE; CStringBuffer sbAssemblyDirectory, sbManifestPath; // // If we're scanning, then we don't want to bother sxs-sfc with changes, // now do we? // // REVIEW: Handy way to get around sfc-sxs... start a series of scans, and // insert 'bad' files into assemblies while we're scanning. // ::SxsProtectionEnableProcessing(FALSE); bSuccess = TRUE; IFW32FALSE_EXIT(::SxspOpenAssemblyInstallationKey( 0, KEY_READ, hkInstallRoot )); dwKeyIndex = 0; while (!fNoMoreKeys) { CAssemblyRecoveryInfo ri; bool fHasAssociatedAssembly; IFW32FALSE_EXIT(hkInstallRoot.EnumKey(dwKeyIndex++, sbKeyName, NULL, &fNoMoreKeys)); if (fNoMoreKeys) { break; } IFW32FALSE_EXIT(ri.AssociateWithAssembly(sbKeyName, fHasAssociatedAssembly)); if (!fHasAssociatedAssembly) { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS.DLL: %s() - We found the assembly %ls in the registry, but were not able to associate it with an assembly\n", __FUNCTION__, static_cast(sbKeyName)); } else if (!ri.GetHasCatalog()) { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS.DLL: %s() - Assembly %ls in registry, no catalog, not validating.\n", __FUNCTION__, static_cast(sbKeyName)); } else { DWORD dwValidateMode, dwResult; SxsRecoveryResult RecoverResult; dwValidateMode = SXS_VALIDATE_ASM_FLAG_CHECK_EVERYTHING; IFW32FALSE_EXIT(::SxspValidateEntireAssembly( dwValidateMode, ri, dwResult)); if (dwResult != SXS_VALIDATE_ASM_FLAG_VALID_PERFECT) { #if DBG ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP | FUSION_DBG_LEVEL_ERROR, "SXS.DLL: %s() - Scan of %ls failed one or more, flagset 0x%08x\n", __FUNCTION__, static_cast(sbKeyName), dwResult); #endif IFW32FALSE_EXIT( ::SxspRecoverAssembly( ri, RecoverResult)); #if DBG if (RecoverResult != Recover_OK) { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP | FUSION_DBG_LEVEL_ERROR, "SXS.DLL: %s() - Reinstallation of assembly %ls failed, status %ls\n", static_cast(sbKeyName), ::SxspRecoveryResultToString(RecoverResult)); } #endif } } } bSuccess = TRUE; Exit: return bSuccess; } BOOL SxsProtectionPerformScanNow( HWND hwProgressWindow, BOOL bValidate, BOOL bUIAllowed ) { BOOL bSuccess = TRUE; bSuccess = ::SxsProtectionPerformScanNowNoSEH(hwProgressWindow, bValidate, bUIAllowed); // // Always reenable sfc notifications! // ::SxsProtectionEnableProcessing(TRUE); return bSuccess; }