#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