/
githubmirror
/
PowerShell
Обзор
Документация
Войти
/
githubmirror
/
PowerShell
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/System.Management.Automation/security/CatalogHelper.cs
841 строка
39 KB
xtqqczze
Enable CA1868: Unnecessary call to 'Contains' for sets (#21165)
29 фев 2024, 20:56
Не верифицирован
29 фев 2024, 20:56
0e106fa
Код
Авторство
О чём код?
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #if !UNIX using System.Security.Cryptography; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; using System.Management.Automation.Internal; using System.Management.Automation.Security; using System.Management.Automation.Win32Native; using System.Runtime.InteropServices; namespace System.Management.Automation { /// <summary> /// Defines the possible status when validating integrity of catalog. /// </summary> public enum CatalogValidationStatus { /// <summary> /// Status when catalog is not tampered. /// </summary> Valid, /// <summary> /// Status when catalog is tampered. /// </summary> ValidationFailed } /// <summary> /// Object returned by Catalog Cmdlets. /// </summary> public class CatalogInformation { /// <summary> /// Status of catalog. /// </summary> public CatalogValidationStatus Status { get; set; } /// <summary> /// Hash Algorithm used to calculate the hashes of files in Catalog. /// </summary> public string HashAlgorithm { get; set; } /// <summary> /// Dictionary mapping files relative paths to their hash values found from Catalog. /// </summary> public Dictionary<string, string> CatalogItems { get; set; } /// <summary> /// Dictionary mapping files relative paths to their hash values. /// </summary> public Dictionary<string, string> PathItems { get; set; } /// <summary> /// Signature for the catalog. /// </summary> public Signature Signature { get; set; } } /// <summary> /// Helper functions for Windows Catalog functionality. /// </summary> internal static class CatalogHelper { // Catalog Version is (0X100 = 256) for Catalog Version 1 private const int catalogVersion1 = 256; // Catalog Version is (0X200 = 512) for Catalog Version 2 private const int catalogVersion2 = 512; // Hash Algorithms supported by Windows Catalog private const string HashAlgorithmSHA1 = "SHA1"; private const string HashAlgorithmSHA256 = "SHA256"; private static PSCmdlet _cmdlet = null; /// <summary> /// Find out the Version of Catalog by reading its Meta data. We can have either version 1 or version 2 catalog. /// </summary> /// <param name="catalogHandle">Handle to open catalog file.</param> /// <returns>Version of the catalog.</returns> private static int GetCatalogVersion(SafeCATHandle catalogHandle) { int catalogVersion = -1; WinTrustMethods.CRYPTCATSTORE catalogInfo = WinTrustMethods.CryptCATStoreFromHandle(catalogHandle); if (catalogInfo.dwPublicVersion == catalogVersion2) { catalogVersion = 2; } // One Windows 7 this API sent version information as decimal 1 not hex (0X100 = 256) // so we are checking for that value as well. Reason we are not checking for version 2 above in // this scenario because catalog version 2 is not supported on win7. else if ((catalogInfo.dwPublicVersion == catalogVersion1) || (catalogInfo.dwPublicVersion == 1)) { catalogVersion = 1; } else { // catalog version we don't understand Exception exception = new InvalidOperationException(StringUtil.Format(CatalogStrings.UnKnownCatalogVersion, catalogVersion1.ToString("X"), catalogVersion2.ToString("X"))); ErrorRecord errorRecord = new ErrorRecord(exception, "UnKnownCatalogVersion", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); } return catalogVersion; } /// <summary> /// HashAlgorithm used by the Catalog. It is based on the version of Catalog. /// </summary> /// <param name="catalogVersion">Path of the output catalog file.</param> /// <returns>Version of the catalog.</returns> private static string GetCatalogHashAlgorithm(int catalogVersion) { string hashAlgorithm = string.Empty; if (catalogVersion == 1) { hashAlgorithm = HashAlgorithmSHA1; } else if (catalogVersion == 2) { hashAlgorithm = HashAlgorithmSHA256; } else { // version we don't understand Exception exception = new InvalidOperationException(StringUtil.Format(CatalogStrings.UnKnownCatalogVersion, "1.0", "2.0")); ErrorRecord errorRecord = new ErrorRecord(exception, "UnKnownCatalogVersion", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); } return hashAlgorithm; } /// <summary> /// Generate the Catalog Definition File representing files and folders. /// </summary> /// <param name="Path">Path of expected output .cdf file.</param> /// <param name="catalogFilePath">Path of the output catalog file.</param> /// <param name="cdfFilePath">Path of the catalog definition file.</param> /// <param name="catalogVersion">Version of catalog.</param> /// <param name="hashAlgorithm">Hash method used to generate hashes for the Catalog.</param> /// <returns>HashSet for the relative Path for files in Catalog.</returns> internal static string GenerateCDFFile(Collection<string> Path, string catalogFilePath, string cdfFilePath, int catalogVersion, string hashAlgorithm) { HashSet<string> relativePaths = new HashSet<string>(); string cdfHeaderContent = string.Empty; string cdfFilesContent = string.Empty; int catAttributeCount = 0; // First create header and files section for the catalog then write in file cdfHeaderContent += "[CatalogHeader]" + Environment.NewLine; cdfHeaderContent += @"Name=" + catalogFilePath + Environment.NewLine; cdfHeaderContent += "CatalogVersion=" + catalogVersion + Environment.NewLine; cdfHeaderContent += "HashAlgorithms=" + hashAlgorithm + Environment.NewLine; cdfFilesContent += "[CatalogFiles]" + Environment.NewLine; foreach (string catalogFile in Path) { if (System.IO.Directory.Exists(catalogFile)) { var directoryItems = Directory.EnumerateFiles(catalogFile, "*.*", SearchOption.AllDirectories); foreach (string fileItem in directoryItems) { ProcessFileToBeAddedInCatalogDefinitionFile(new FileInfo(fileItem), new DirectoryInfo(catalogFile), ref relativePaths, ref cdfHeaderContent, ref cdfFilesContent, ref catAttributeCount); } } else if (System.IO.File.Exists(catalogFile)) { ProcessFileToBeAddedInCatalogDefinitionFile(new FileInfo(catalogFile), null, ref relativePaths, ref cdfHeaderContent, ref cdfFilesContent, ref catAttributeCount); } } using (System.IO.StreamWriter fileWriter = new System.IO.StreamWriter(new FileStream(cdfFilePath, FileMode.Create))) { fileWriter.WriteLine(cdfHeaderContent); fileWriter.WriteLine(); fileWriter.WriteLine(cdfFilesContent); } return cdfFilePath; } /// <summary> /// Get file attribute (Relative path in our case) from catalog. /// </summary> /// <param name="fileToHash">File to hash.</param> /// <param name="dirInfo">Directory information about file needed to calculate relative file path.</param> /// <param name="relativePaths">Working set of relative paths of all files.</param> /// <param name="cdfHeaderContent">Content to be added in CatalogHeader section of cdf File.</param> /// <param name="cdfFilesContent">Content to be added in CatalogFiles section of cdf File.</param> /// <param name="catAttributeCount">Indicating the current no of catalog header level attributes.</param> /// <returns>Void.</returns> internal static void ProcessFileToBeAddedInCatalogDefinitionFile(FileInfo fileToHash, DirectoryInfo dirInfo, ref HashSet<string> relativePaths, ref string cdfHeaderContent, ref string cdfFilesContent, ref int catAttributeCount) { string relativePath = string.Empty; if (dirInfo != null) { // Relative path of the file is the path inside the containing folder excluding folder Name relativePath = fileToHash.FullName.AsSpan(dirInfo.FullName.Length).TrimStart('\\').ToString(); } else { relativePath = fileToHash.Name; } if (relativePaths.Add(relativePath)) { if (fileToHash.Length != 0) { cdfFilesContent += "<HASH>" + fileToHash.FullName + "=" + fileToHash.FullName + Environment.NewLine; cdfFilesContent += "<HASH>" + fileToHash.FullName + "ATTR1=0x10010001:FilePath:" + relativePath + Environment.NewLine; } else { // zero length files are added as catalog level attributes because they can not be hashed cdfHeaderContent += "CATATTR" + (++catAttributeCount) + "=0x10010001:FilePath:" + relativePath + Environment.NewLine; } } else { // If Files have same relative paths we can not distinguish them for // Validation. So failing. ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.FoundDuplicateFilesRelativePath, relativePath)), "FoundDuplicateFilesRelativePath", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); } } /// <summary> /// Generate the Catalog file for Input Catalog Definition File. /// </summary> /// <param name="cdfFilePath">Path to the Input .cdf file.</param> internal static void GenerateCatalogFile(string cdfFilePath) { // Open CDF File SafeCATCDFHandle resultCDF; try { resultCDF = WinTrustMethods.CryptCATCDFOpen(cdfFilePath, ParseErrorCallback); } catch (Win32Exception e) { // If we are not able to open CDF file we can not continue generating catalog ErrorRecord errorRecord = new ErrorRecord( new InvalidOperationException(CatalogStrings.UnableToOpenCatalogDefinitionFile, e), "UnableToOpenCatalogDefinitionFile", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); return; } // navigate CDF header and files sections using (resultCDF) { // First navigate all catalog level attributes entries first, they represent zero size files IntPtr catalogAttr = IntPtr.Zero; do { catalogAttr = WinTrustMethods.CryptCATCDFEnumCatAttributes(resultCDF, catalogAttr, ParseErrorCallback); if (catalogAttr != IntPtr.Zero) { string filePath = ProcessFilePathAttributeInCatalog(catalogAttr); _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.AddFileToCatalog, filePath, filePath)); } } while (catalogAttr != IntPtr.Zero); // navigate all the files hash entries in the .cdf file IntPtr memberInfo = IntPtr.Zero; IntPtr memberFile = IntPtr.Zero; string fileName = string.Empty; do { memberFile = WinTrustMethods.CryptCATCDFEnumMembersByCDFTagEx(resultCDF, memberFile, ParseErrorCallback, ref memberInfo, fContinueOnError: true, pvReserved: IntPtr.Zero); fileName = Marshal.PtrToStringUni(memberFile); if (!string.IsNullOrEmpty(fileName)) { IntPtr memberAttr = IntPtr.Zero; string fileRelativePath = string.Empty; do { memberAttr = WinTrustMethods.CryptCATCDFEnumAttributesWithCDFTag(resultCDF, memberFile, memberInfo, memberAttr, ParseErrorCallback); if (memberAttr != IntPtr.Zero) { fileRelativePath = ProcessFilePathAttributeInCatalog(memberAttr); if (!string.IsNullOrEmpty(fileRelativePath)) { // Found the attribute we are looking for // Filename we read from the above API has <Hash> appended to its name as per CDF file tags convention // Truncating that Information from the string. string itemName = fileName.Substring(6); _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.AddFileToCatalog, itemName, fileRelativePath)); break; } } } while (memberAttr != IntPtr.Zero); } } while (fileName != null); } } /// <summary> /// To generate Catalog for the folder. /// </summary> /// <param name="Path">Path to folder or File.</param> /// <param name="catalogFilePath">Catalog File Path.</param> /// <param name="catalogVersion">Catalog File Path.</param> /// <param name="cmdlet">Instance of cmdlet calling this method.</param> /// <returns>True if able to generate .cat file or false.</returns> internal static FileInfo GenerateCatalog(PSCmdlet cmdlet, Collection<string> Path, string catalogFilePath, int catalogVersion) { _cmdlet = cmdlet; string hashAlgorithm = GetCatalogHashAlgorithm(catalogVersion); if (!string.IsNullOrEmpty(hashAlgorithm)) { // Generate Path for Catalog Definition File string cdfFilePath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetRandomFileName()); cdfFilePath += ".cdf"; try { cdfFilePath = GenerateCDFFile(Path, catalogFilePath, cdfFilePath, catalogVersion, hashAlgorithm); if (!File.Exists(cdfFilePath)) { // If we are not able to generate catalog definition file we can not continue generating catalog // throw PSTraceSource.NewInvalidOperationException("catalog", CatalogStrings.CatalogDefinitionFileNotGenerated); ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(CatalogStrings.CatalogDefinitionFileNotGenerated), "CatalogDefinitionFileNotGenerated", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); } GenerateCatalogFile(cdfFilePath); if (File.Exists(catalogFilePath)) { return new FileInfo(catalogFilePath); } } finally { File.Delete(cdfFilePath); } } return null; } /// <summary> /// Get file attribute (Relative path in our case) from catalog. /// </summary> /// <param name="memberAttrInfo">Pointer to current attribute of catalog member.</param> /// <returns>Value of the attribute.</returns> internal static string ProcessFilePathAttributeInCatalog(IntPtr memberAttrInfo) { string relativePath = string.Empty; WinTrustMethods.CRYPTCATATTRIBUTE currentMemberAttr = Marshal.PtrToStructure<WinTrustMethods.CRYPTCATATTRIBUTE>(memberAttrInfo); // check if this is the attribute we are looking for // catalog generated other way not using New-FileCatalog can have attributes we don't understand if (currentMemberAttr.pwszReferenceTag.Equals("FilePath", StringComparison.OrdinalIgnoreCase)) { // find the size for the current attribute value and then allocate buffer and copy from byte array int attrValueSize = (int)currentMemberAttr.cbValue; byte[] attrValue = new byte[attrValueSize]; Marshal.Copy(currentMemberAttr.pbValue, attrValue, 0, attrValueSize); relativePath = System.Text.Encoding.Unicode.GetString(attrValue); relativePath = relativePath.TrimEnd('\0'); } return relativePath; } /// <summary> /// Make a hash for the file. /// </summary> /// <param name="filePath">Path of the file.</param> /// <param name="hashAlgorithm">Used to calculate Hash.</param> /// <returns>HashValue for the file.</returns> internal static string CalculateFileHash(string filePath, string hashAlgorithm) { string hashValue = string.Empty; // To get handle to the hash algorithm to be used to calculate hashes SafeCATAdminHandle catAdmin; try { catAdmin = WinTrustMethods.CryptCATAdminAcquireContext2(hashAlgorithm); } catch (Win32Exception e) { ErrorRecord errorRecord = new ErrorRecord( new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToAcquireHashAlgorithmContext, hashAlgorithm), e), "UnableToAcquireHashAlgorithmContext", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); // The method returns an empty string on a failure. return hashValue; } // Open the file that is to be hashed for reading and get its handle FileStream fileStream; try { fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); } catch (Exception e) { // If we are not able to open file that is to be hashed we can not continue with catalog validation ErrorRecord errorRecord = new ErrorRecord( new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToReadFileToHash, filePath), e), "UnableToReadFileToHash", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); // The method returns an empty string on a failure. return hashValue; } using (catAdmin) using (fileStream) { byte[] hashBytes = Array.Empty<byte>(); try { hashBytes = WinTrustMethods.CryptCATAdminCalcHashFromFileHandle2(catAdmin, fileStream.SafeFileHandle); } catch (Win32Exception e) { ErrorRecord errorRecord = new ErrorRecord( new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToCreateFileHash, filePath), e), "UnableToCreateFileHash", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); } hashValue = Convert.ToHexString(hashBytes); } return hashValue; } /// <summary> /// Make list of hashes for given Catalog File. /// </summary> /// <param name="catalogFilePath">Path to the folder having catalog file.</param> /// <param name="excludedPatterns"></param> /// <param name="catalogVersion">The version of input catalog we read from catalog meta data after opening it.</param> /// <returns>Dictionary mapping files relative paths to HashValues.</returns> internal static Dictionary<string, string> GetHashesFromCatalog(string catalogFilePath, WildcardPattern[] excludedPatterns, out int catalogVersion) { Dictionary<string, string> catalogHashes = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase); catalogVersion = 0; SafeCATHandle resultCatalog; try { resultCatalog = WinTrustMethods.CryptCATOpen(catalogFilePath, 0, IntPtr.Zero, 1, 0); } catch (Win32Exception e) { ErrorRecord errorRecord = new ErrorRecord( new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToOpenCatalogFile, catalogFilePath), e), "UnableToOpenCatalogFile", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); return catalogHashes; } using (resultCatalog) { IntPtr catAttrInfo = IntPtr.Zero; // First traverse all catalog level attributes to get information about zero size file. do { catAttrInfo = WinTrustMethods.CryptCATEnumerateCatAttr(resultCatalog, catAttrInfo); // If we found attribute it is a file information retrieve its relative path // and add it to catalog hash collection if its not in excluded files criteria if (catAttrInfo != IntPtr.Zero) { string relativePath = ProcessFilePathAttributeInCatalog(catAttrInfo); if (!string.IsNullOrEmpty(relativePath)) { ProcessCatalogFile(relativePath, string.Empty, excludedPatterns, ref catalogHashes); } } } while (catAttrInfo != IntPtr.Zero); catalogVersion = GetCatalogVersion(resultCatalog); IntPtr memberInfo = IntPtr.Zero; // Next Navigate all members in Catalog files and get their relative paths and hashes do { memberInfo = WinTrustMethods.CryptCATEnumerateMember(resultCatalog, memberInfo); if (memberInfo != IntPtr.Zero) { WinTrustMethods.CRYPTCATMEMBER currentMember = Marshal.PtrToStructure<WinTrustMethods.CRYPTCATMEMBER>(memberInfo); WinTrustMethods.SIP_INDIRECT_DATA pIndirectData = Marshal.PtrToStructure<WinTrustMethods.SIP_INDIRECT_DATA>(currentMember.pIndirectData); // For Catalog version 2 CryptoAPI puts hashes of file attributes(relative path in our case) in Catalog as well // We validate those along with file hashes so we are skipping duplicate entries if (!((catalogVersion == 2) && (pIndirectData.DigestAlgorithm.pszObjId.Equals(new Oid("SHA1").Value, StringComparison.OrdinalIgnoreCase)))) { string relativePath = string.Empty; IntPtr memberAttrInfo = IntPtr.Zero; do { memberAttrInfo = WinTrustMethods.CryptCATEnumerateAttr(resultCatalog, memberInfo, memberAttrInfo); if (memberAttrInfo != IntPtr.Zero) { relativePath = ProcessFilePathAttributeInCatalog(memberAttrInfo); if (!string.IsNullOrEmpty(relativePath)) { break; } } } while (memberAttrInfo != IntPtr.Zero); // If we did not find any Relative Path for the item in catalog we should quit // This catalog must not be valid for our use as catalogs generated using New-FileCatalog // always contains relative file Paths if (string.IsNullOrEmpty(relativePath)) { ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToOpenCatalogFile, catalogFilePath)), "UnableToOpenCatalogFile", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); } ProcessCatalogFile(relativePath, currentMember.pwszReferenceTag, excludedPatterns, ref catalogHashes); } } } while (memberInfo != IntPtr.Zero); } return catalogHashes; } /// <summary> /// Process file in path for its relative paths. /// </summary> /// <param name="relativePath">Relative path of file found in catalog.</param> /// <param name="fileHash">Hash of file found in catalog.</param> /// <param name="excludedPatterns">Skip file from validation if it matches these patterns.</param> /// <param name="catalogHashes">Collection of hashes of catalog.</param> /// <returns>Void.</returns> internal static void ProcessCatalogFile(string relativePath, string fileHash, WildcardPattern[] excludedPatterns, ref Dictionary<string, string> catalogHashes) { // Found the attribute we are looking for _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.FoundFileHashInCatalogItem, relativePath, fileHash)); // Only add the file for validation if it does not meet exclusion criteria if (!CheckExcludedCriteria((new FileInfo(relativePath)).Name, excludedPatterns)) { // Add relativePath mapping to hashvalue for each file catalogHashes.Add(relativePath, fileHash); } else { // Verbose about skipping file from catalog _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.SkipValidationOfCatalogFile, relativePath)); } } /// <summary> /// Process file in path for its relative paths. /// </summary> /// <param name="fileToHash">File to hash.</param> /// <param name="dirInfo">Directory information about file needed to calculate relative file path.</param> /// <param name="hashAlgorithm">Used to calculate Hash.</param> /// <param name="excludedPatterns">Skip file if it matches these patterns.</param> /// <param name="fileHashes">Collection of hashes of files.</param> /// <returns>Void.</returns> internal static void ProcessPathFile(FileInfo fileToHash, DirectoryInfo dirInfo, string hashAlgorithm, WildcardPattern[] excludedPatterns, ref Dictionary<string, string> fileHashes) { string relativePath = string.Empty; string exclude = string.Empty; if (dirInfo != null) { // Relative path of the file is the path inside the containing folder excluding folder Name relativePath = fileToHash.FullName.AsSpan(dirInfo.FullName.Length).TrimStart('\\').ToString(); exclude = fileToHash.Name; } else { relativePath = fileToHash.Name; exclude = relativePath; } if (!CheckExcludedCriteria(exclude, excludedPatterns)) { string fileHash = string.Empty; if (fileToHash.Length != 0) { fileHash = CalculateFileHash(fileToHash.FullName, hashAlgorithm); } if (fileHashes.TryAdd(relativePath, fileHash)) { _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.FoundFileInPath, relativePath, fileHash)); } else { ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.FoundDuplicateFilesRelativePath, relativePath)), "FoundDuplicateFilesRelativePath", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); } } else { // Verbose about skipping file from path _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.SkipValidationOfPathFile, relativePath)); } } /// <summary> /// Generate the hashes of all the files in given folder. /// </summary> /// <param name="folderPaths">Path to folder or File.</param> /// <param name="catalogFilePath">Catalog file path it should be skipped when calculating the hashes.</param> /// <param name="hashAlgorithm">Used to calculate Hash.</param> /// <param name="excludedPatterns"></param> /// <returns>Dictionary mapping file relative paths to hashes..</returns> internal static Dictionary<string, string> CalculateHashesFromPath(Collection<string> folderPaths, string catalogFilePath, string hashAlgorithm, WildcardPattern[] excludedPatterns) { // Create a HashTable of file Hashes Dictionary<string, string> fileHashes = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase); foreach (string folderPath in folderPaths) { if (System.IO.Directory.Exists(folderPath)) { var directoryItems = Directory.EnumerateFiles(folderPath, "*.*", SearchOption.AllDirectories); foreach (string fileItem in directoryItems) { // if its the catalog file we are validating we will skip it if (string.Equals(fileItem, catalogFilePath, StringComparison.OrdinalIgnoreCase)) continue; ProcessPathFile(new FileInfo(fileItem), new DirectoryInfo(folderPath), hashAlgorithm, excludedPatterns, ref fileHashes); } } else if (System.IO.File.Exists(folderPath)) { ProcessPathFile(new FileInfo(folderPath), null, hashAlgorithm, excludedPatterns, ref fileHashes); } } return fileHashes; } /// <summary> /// Compare Dictionary objects. /// </summary> /// <param name="catalogItems">Hashes extracted from Catalog.</param> /// <param name="pathItems">Hashes created from folders path.</param> /// <returns>True if both collections are same.</returns> internal static bool CompareDictionaries(Dictionary<string, string> catalogItems, Dictionary<string, string> pathItems) { bool Status = true; List<string> relativePathsFromFolder = pathItems.Keys.ToList(); List<string> relativePathsFromCatalog = catalogItems.Keys.ToList(); // Find entries that are not in both lists. These should be empty lists for success // Hashes in Catalog should be exactly similar to the ones from folder List<string> relativePathsNotInFolder = relativePathsFromFolder.Except(relativePathsFromCatalog, StringComparer.CurrentCultureIgnoreCase).ToList(); List<string> relativePathsNotInCatalog = relativePathsFromCatalog.Except(relativePathsFromFolder, StringComparer.CurrentCultureIgnoreCase).ToList(); // Found extra hashes in Folder if ((relativePathsNotInFolder.Count != 0) || (relativePathsNotInCatalog.Count != 0)) { Status = false; } foreach (KeyValuePair<string, string> item in catalogItems) { string catalogHashValue = (string)catalogItems[item.Key]; if (pathItems.ContainsKey(item.Key)) { string folderHashValue = (string)pathItems[item.Key]; if (folderHashValue.Equals(catalogHashValue)) { continue; } else { Status = false; } } } return Status; } /// <summary> /// To Validate the Integrity of Catalog. /// </summary> /// <param name="catalogFolders">Folder for which catalog is created.</param> /// <param name="catalogFilePath">File Name of the Catalog.</param> /// <param name="excludedPatterns"></param> /// <param name="cmdlet">Instance of cmdlet calling this method.</param> /// <returns>Information about Catalog.</returns> internal static CatalogInformation ValidateCatalog(PSCmdlet cmdlet, Collection<string> catalogFolders, string catalogFilePath, WildcardPattern[] excludedPatterns) { _cmdlet = cmdlet; int catalogVersion = 0; Dictionary<string, string> catalogHashes = GetHashesFromCatalog(catalogFilePath, excludedPatterns, out catalogVersion); string hashAlgorithm = GetCatalogHashAlgorithm(catalogVersion); if (!string.IsNullOrEmpty(hashAlgorithm)) { Dictionary<string, string> fileHashes = CalculateHashesFromPath(catalogFolders, catalogFilePath, hashAlgorithm, excludedPatterns); CatalogInformation catalog = new CatalogInformation(); catalog.CatalogItems = catalogHashes; catalog.PathItems = fileHashes; bool status = CompareDictionaries(catalogHashes, fileHashes); if (status) { catalog.Status = CatalogValidationStatus.Valid; } else { catalog.Status = CatalogValidationStatus.ValidationFailed; } catalog.HashAlgorithm = hashAlgorithm; catalog.Signature = SignatureHelper.GetSignature(catalogFilePath, null); return catalog; } return null; } /// <summary> /// Check if file meets the skip validation criteria. /// </summary> /// <param name="filename"></param> /// <param name="excludedPatterns"></param> /// <returns>True if match is found else false.</returns> internal static bool CheckExcludedCriteria(string filename, WildcardPattern[] excludedPatterns) { if (excludedPatterns != null) { foreach (WildcardPattern patternItem in excludedPatterns) { if (patternItem.IsMatch(filename)) { return true; } } } return false; } /// <summary> /// Call back when error is thrown by catalog API's. /// </summary> private static void ParseErrorCallback(uint dwErrorArea, uint dwLocalError, string pwszLine) { switch (dwErrorArea) { case NativeConstants.CRYPTCAT_E_AREA_HEADER: break; case NativeConstants.CRYPTCAT_E_AREA_MEMBER: break; case NativeConstants.CRYPTCAT_E_AREA_ATTRIBUTE: break; default: break; } switch (dwLocalError) { case NativeConstants.CRYPTCAT_E_CDF_MEMBER_FILE_PATH: { ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToFindFileNameOrPathForCatalogMember, pwszLine)), "UnableToFindFileNameOrPathForCatalogMember", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); break; } case NativeConstants.CRYPTCAT_E_CDF_MEMBER_INDIRECTDATA: { ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToCreateFileHash, pwszLine)), "UnableToCreateFileHash", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); break; } case NativeConstants.CRYPTCAT_E_CDF_MEMBER_FILENOTFOUND: { ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToFindFileToHash, pwszLine)), "UnableToFindFileToHash", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); break; } case NativeConstants.CRYPTCAT_E_CDF_BAD_GUID_CONV: break; case NativeConstants.CRYPTCAT_E_CDF_ATTR_TYPECOMBO: break; case NativeConstants.CRYPTCAT_E_CDF_ATTR_TOOFEWVALUES: break; case NativeConstants.CRYPTCAT_E_CDF_UNSUPPORTED: break; case NativeConstants.CRYPTCAT_E_CDF_DUPLICATE: { ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.FoundDuplicateFileMemberInCatalog, pwszLine)), "FoundDuplicateFileMemberInCatalog", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); break; } case NativeConstants.CRYPTCAT_E_CDF_TAGNOTFOUND: break; default: break; } } } } #endif