#if !UNIX /********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ using Dbg = System.Management.Automation; using System; using System.Text; using System.Security.Cryptography; using System.Collections.Generic; using System.Collections; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; using System.Management.Automation.Internal; using System.Management.Automation.Provider; using System.Management.Automation.Security; using System.Runtime.InteropServices; using DWORD = System.UInt32; namespace System.Management.Automation { /// /// Defines the possible status when validating integrity of catalog /// public enum CatalogValidationStatus { /// /// Status when catalog is not tampered. /// Valid, /// /// Status when catalog is tampered. /// ValidationFailed } /// /// Object returned by Catalog Cmdlets /// public class CatalogInformation { /// /// status of catalog /// public CatalogValidationStatus Status { get; set; } /// /// Hash Algorithm used to calculate the hashes of files in Catalog /// public string HashAlgorithm { get; set; } /// /// Dictionary mapping files relative paths to their hash values found from Catalog /// public Dictionary CatalogItems { get; set; } /// /// Dictionary mapping files relative paths to their hash values /// public Dictionary PathItems { get; set; } /// /// Signature for the catalog /// public Signature Signature { get; set; } } /// /// Helper functions for Windows Catalog functionality /// internal static class CatalogHelper { // Catalog Version is (0X100 = 256) for Catalog Version 1 private static int catalogVersion1 = 256; // Catalog Version is (0X200 = 512) for Catalog Version 2 private static int catalogVersion2 = 512; // Hash Algorithms supported by Windows Catalog private static string HashAlgorithmSHA1 = "SHA1"; private static string HashAlgorithmSHA256 = "SHA256"; private static PSCmdlet _cmdlet = null; /// /// Find out the Version of Catalog by reading its Meta data. We can have either version 1 or version 2 catalog /// /// Handle to open catalog file /// Version of the catalog private static int GetCatalogVersion(IntPtr catalogHandle) { int catalogVersion = -1; IntPtr catalogData = NativeMethods.CryptCATStoreFromHandle(catalogHandle); NativeMethods.CRYPTCATSTORE catalogInfo = Marshal.PtrToStructure(catalogData); 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; } /// /// HashAlgorithm used by the Catalog. It is based on the version of Catalog. /// /// Path of the output catalog file /// Version of the catalog 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; } /// /// Generate the Catalog Definition File representing files and folders /// /// /// Path of expected output .cdf file /// /// Path of the output catalog file /// /// Path of the catalog definition file /// /// Version of catalog /// /// hash method used to generate hashes for the Catalog /// HashSet for the relative Path for files in Catalog internal static string GenerateCDFFile(Collection Path, string catalogFilePath, string cdfFilePath, int catalogVersion, string hashAlgorithm) { HashSet relativePaths = new HashSet(); 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; } /// /// Get file attribute (Relative path in our case) from catalog /// /// file to hash /// directory information about file needed to calculate relative file path. /// working set of relative paths of all files. /// content to be added in CatalogHeader section of cdf File /// content to be added in CatalogFiles section of cdf File /// indicating the current no of catalog header level attributes /// void internal static void ProcessFileToBeAddedInCatalogDefinitionFile(FileInfo fileToHash, DirectoryInfo dirInfo, ref HashSet 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.Substring(dirInfo.FullName.Length).TrimStart('\\'); } else { relativePath = fileToHash.Name; } if (!relativePaths.Contains(relativePath)) { relativePaths.Add(relativePath); if (fileToHash.Length != 0) { cdfFilesContent += "" + fileToHash.FullName + "=" + fileToHash.FullName + Environment.NewLine; cdfFilesContent += "" + 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); } } /// /// Generate the Catalog file for Input Catalog Definition File /// /// /// Path to the Input .cdf file internal static void GenerateCatalogFile(string cdfFilePath) { string pwszFilePath = cdfFilePath; NativeMethods.CryptCATCDFOpenCallBack catOpenCallBack = new NativeMethods.CryptCATCDFOpenCallBack(ParseErrorCallback); // Open CDF File IntPtr resultCDF = NativeMethods.CryptCATCDFOpen(pwszFilePath, catOpenCallBack); // navigate CDF header and files sections if (resultCDF != IntPtr.Zero) { // First navigate all catalog level attributes entries first, they represent zero size files IntPtr catalogAttr = IntPtr.Zero; do { catalogAttr = NativeMethods.CryptCATCDFEnumCatAttributes(resultCDF, catalogAttr, catOpenCallBack); 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; try { IntPtr memberFile = IntPtr.Zero; NativeMethods.CryptCATCDFEnumMembersByCDFTagExErrorCallBack memberCallBack = new NativeMethods.CryptCATCDFEnumMembersByCDFTagExErrorCallBack(ParseErrorCallback); string fileName = string.Empty; do { memberFile = NativeMethods.CryptCATCDFEnumMembersByCDFTagEx(resultCDF, memberFile, memberCallBack, ref memberInfo, true, IntPtr.Zero); fileName = Marshal.PtrToStringUni(memberFile); if (!String.IsNullOrEmpty(fileName)) { IntPtr memberAttr = IntPtr.Zero; string fileRelativePath = String.Empty; do { memberAttr = NativeMethods.CryptCATCDFEnumAttributesWithCDFTag(resultCDF, memberFile, memberInfo, memberAttr, memberCallBack); 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 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); } finally { NativeMethods.CryptCATCDFClose(resultCDF); } } else { // If we are not able to open CDF file we can not continue generating catalog ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(CatalogStrings.UnableToOpenCatalogDefinitionFile), "UnableToOpenCatalogDefinitionFile", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); } } /// /// To generate Catalog for the folder /// /// /// Path to folder or File /// Catalog File Path /// Catalog File Path /// Instance of cmdlet calling this method /// true if able to generate .cat file or false internal static FileInfo GenerateCatalog(PSCmdlet cmdlet, Collection 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 = 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; } /// /// Get file attribute (Relative path in our case) from catalog /// /// Pointer to current attribute of catalog member. /// value of the attribute internal static string ProcessFilePathAttributeInCatalog(IntPtr memberAttrInfo) { string relativePath = string.Empty; NativeMethods.CRYPTCATATTRIBUTE currentMemberAttr = Marshal.PtrToStructure(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; } /// /// Make a hash for the file /// /// /// Path of the file /// Used to calculate Hash /// HashValue for the file internal static string CalculateFileHash(string filePath, string hashAlgorithm) { string hashValue = string.Empty; IntPtr catAdmin = IntPtr.Zero; // To get handle to the hash algorithm to be used to calculate hashes if (!NativeMethods.CryptCATAdminAcquireContext2(ref catAdmin, IntPtr.Zero, hashAlgorithm, IntPtr.Zero, 0)) { ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToAcquireHashAlgorithmContext, hashAlgorithm)), "UnableToAcquireHashAlgorithmContext", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); } DWORD GENERIC_READ = 0x80000000; DWORD OPEN_EXISTING = 3; IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); // Open the file that is to be hashed for reading and get its handle IntPtr fileHandle = NativeMethods.CreateFile(filePath, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, IntPtr.Zero); if (fileHandle != INVALID_HANDLE_VALUE) { try { DWORD hashBufferSize = 0; IntPtr hashBuffer = IntPtr.Zero; // Call first time to get the size of expected buffer to hold new hash value if (!NativeMethods.CryptCATAdminCalcHashFromFileHandle2(catAdmin, fileHandle, ref hashBufferSize, hashBuffer, 0)) { ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToCreateFileHash, filePath)), "UnableToCreateFileHash", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); } int size = (int)hashBufferSize; hashBuffer = Marshal.AllocHGlobal(size); try { // Call second time to actually get the hash value if (!NativeMethods.CryptCATAdminCalcHashFromFileHandle2(catAdmin, fileHandle, ref hashBufferSize, hashBuffer, 0)) { ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToCreateFileHash, filePath)), "UnableToCreateFileHash", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); } byte[] hashBytes = new byte[size]; Marshal.Copy(hashBuffer, hashBytes, 0, size); hashValue = BitConverter.ToString(hashBytes).Replace("-", ""); } finally { if (hashBuffer != IntPtr.Zero) { Marshal.FreeHGlobal(hashBuffer); } } } finally { NativeMethods.CryptCATAdminReleaseContext(catAdmin, 0); NativeMethods.CloseHandle(fileHandle); } } else { // 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)), "UnableToReadFileToHash", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); } return hashValue; } /// /// Make list of hashes for given Catalog File /// /// Path to the folder having catalog file /// /// The version of input catalog we read from catalog meta data after opening it. /// Dictionary mapping files relative paths to HashValues internal static Dictionary GetHashesFromCatalog(string catalogFilePath, WildcardPattern[] excludedPatterns, out int catalogVersion) { IntPtr resultCatalog = NativeMethods.CryptCATOpen(catalogFilePath, 0, IntPtr.Zero, 1, 0); IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); Dictionary catalogHashes = new Dictionary(StringComparer.CurrentCultureIgnoreCase); catalogVersion = 0; if (resultCatalog != INVALID_HANDLE_VALUE) { try { IntPtr catAttrInfo = IntPtr.Zero; // First traverse all catalog level attributes to get information about zero size file. do { catAttrInfo = NativeMethods.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 = NativeMethods.CryptCATEnumerateMember(resultCatalog, memberInfo); if (memberInfo != IntPtr.Zero) { NativeMethods.CRYPTCATMEMBER currentMember = Marshal.PtrToStructure(memberInfo); NativeMethods.SIP_INDIRECT_DATA pIndirectData = Marshal.PtrToStructure(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 = NativeMethods.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); } finally { NativeMethods.CryptCATClose(resultCatalog); } } else { ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToOpenCatalogFile, catalogFilePath)), "UnableToOpenCatalogFile", ErrorCategory.InvalidOperation, null); _cmdlet.ThrowTerminatingError(errorRecord); } return catalogHashes; } /// /// Process file in path for its relative paths /// /// relative path of file found in catalog /// hash of file found in catalog. /// skip file from validation if it matches these patterns /// collection of hashes of catalog /// void internal static void ProcessCatalogFile(string relativePath, string fileHash, WildcardPattern[] excludedPatterns, ref Dictionary 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)); } } /// /// Process file in path for its relative paths /// /// file to hash /// directory information about file needed to calculate relative file path. /// Used to calculate Hash /// skip file if it matches these patterns /// collection of hashes of files /// void internal static void ProcessPathFile(FileInfo fileToHash, DirectoryInfo dirInfo, string hashAlgorithm, WildcardPattern[] excludedPatterns, ref Dictionary 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.Substring(dirInfo.FullName.Length).TrimStart('\\'); 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.ContainsKey(relativePath)) { fileHashes.Add(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)); } } /// /// Generate the hashes of all the files in given folder /// /// /// Path to folder or File /// catalog file path it should be skipped when calculating the hashes /// Used to calculate Hash /// /// Dictionary mapping file relative paths to hashes. internal static Dictionary CalculateHashesFromPath(Collection folderPaths, string catalogFilePath, string hashAlgorithm, WildcardPattern[] excludedPatterns) { // Create a HashTable of file Hashes Dictionary fileHashes = new Dictionary(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; } /// /// Compare Dictionary objects /// /// /// Hashes extracted from Catalog /// /// Hashes created from folders path /// /// True if both collections are same internal static bool CompareDictionaries(Dictionary catalogItems, Dictionary pathItems) { bool Status = true; List relativePathsFromFolder = pathItems.Keys.ToList(); List relativePathsFromCatalog = catalogItems.Keys.ToList(); // Find entires those are not in both list lists. These should be empty lists for success // Hashes in Catalog should be exact similar to the ones from folder List relativePathsNotInFolder = relativePathsFromFolder.Except(relativePathsFromCatalog, StringComparer.CurrentCultureIgnoreCase).ToList(); List relativePathsNotInCatalog = relativePathsFromCatalog.Except(relativePathsFromFolder, StringComparer.CurrentCultureIgnoreCase).ToList(); //Found extra hashes in Folder if ((relativePathsNotInFolder.Count != 0) || (relativePathsNotInCatalog.Count != 0)) { Status = false; } foreach (KeyValuePair 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; } /// /// To Validate the Integrity of Catalog /// /// /// Folder for which catalog is created /// File Name of the Catalog /// /// Instance of cmdlet calling this method /// Information about Catalog internal static CatalogInformation ValidateCatalog(PSCmdlet cmdlet, Collection catalogFolders, String catalogFilePath, WildcardPattern[] excludedPatterns) { _cmdlet = cmdlet; int catalogVersion = 0; Dictionary catalogHashes = GetHashesFromCatalog(catalogFilePath, excludedPatterns, out catalogVersion); string hashAlgorithm = GetCatalogHashAlgorithm(catalogVersion); if (!String.IsNullOrEmpty(hashAlgorithm)) { Dictionary fileHashes = CalculateHashesFromPath(catalogFolders, catalogFilePath, hashAlgorithm, excludedPatterns); CatalogInformation catalog = new CatalogInformation(); catalog.CatalogItems = catalogHashes; catalog.PathItems = fileHashes; bool status = CompareDictionaries(catalogHashes, fileHashes); if (status == true) { catalog.Status = CatalogValidationStatus.Valid; } else { catalog.Status = CatalogValidationStatus.ValidationFailed; } catalog.HashAlgorithm = hashAlgorithm; catalog.Signature = SignatureHelper.GetSignature(catalogFilePath, null); return catalog; } return null; } /// /// Check if file meets the skip validation criteria /// /// /// /// True if match is found else false internal static bool CheckExcludedCriteria(string filename, WildcardPattern[] excludedPatterns) { if (excludedPatterns != null) { foreach (WildcardPattern patternItem in excludedPatterns) { if (patternItem.IsMatch(filename)) { return true; } } } return false; } /// /// Call back when error is thrown by catalog API's /// private static void ParseErrorCallback(DWORD dwErrorArea, DWORD 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