MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4#if !UNIX5 6using System.Security.Cryptography;7using System.Collections.Generic;8using System.Collections.ObjectModel;9using System.ComponentModel;10using System.IO;11using System.Linq;12using System.Management.Automation.Internal;13using System.Management.Automation.Security;14using System.Management.Automation.Win32Native;15using System.Runtime.InteropServices;16 17namespace System.Management.Automation18{19 /// <summary>20 /// Defines the possible status when validating integrity of catalog.21 /// </summary>22 public enum CatalogValidationStatus23 {24 /// <summary>25 /// Status when catalog is not tampered.26 /// </summary>27 Valid,28 29 /// <summary>30 /// Status when catalog is tampered.31 /// </summary>32 ValidationFailed33 }34 35 /// <summary>36 /// Object returned by Catalog Cmdlets.37 /// </summary>38 public class CatalogInformation39 {40 /// <summary>41 /// Status of catalog.42 /// </summary>43 public CatalogValidationStatus Status { get; set; }44 45 /// <summary>46 /// Hash Algorithm used to calculate the hashes of files in Catalog.47 /// </summary>48 public string HashAlgorithm { get; set; }49 50 /// <summary>51 /// Dictionary mapping files relative paths to their hash values found from Catalog.52 /// </summary>53 public Dictionary<string, string> CatalogItems { get; set; }54 55 /// <summary>56 /// Dictionary mapping files relative paths to their hash values.57 /// </summary>58 public Dictionary<string, string> PathItems { get; set; }59 60 /// <summary>61 /// Signature for the catalog.62 /// </summary>63 public Signature Signature { get; set; }64 }65 66 /// <summary>67 /// Helper functions for Windows Catalog functionality.68 /// </summary>69 internal static class CatalogHelper70 {71 // Catalog Version is (0X100 = 256) for Catalog Version 172 private const int catalogVersion1 = 256;73 74 // Catalog Version is (0X200 = 512) for Catalog Version 275 private const int catalogVersion2 = 512;76 77 // Hash Algorithms supported by Windows Catalog78 private const string HashAlgorithmSHA1 = "SHA1";79 private const string HashAlgorithmSHA256 = "SHA256";80 private static PSCmdlet _cmdlet = null;81 82 /// <summary>83 /// Find out the Version of Catalog by reading its Meta data. We can have either version 1 or version 2 catalog.84 /// </summary>85 /// <param name="catalogHandle">Handle to open catalog file.</param>86 /// <returns>Version of the catalog.</returns>87 private static int GetCatalogVersion(SafeCATHandle catalogHandle)88 {89 int catalogVersion = -1;90 91 WinTrustMethods.CRYPTCATSTORE catalogInfo = WinTrustMethods.CryptCATStoreFromHandle(catalogHandle);92 93 if (catalogInfo.dwPublicVersion == catalogVersion2)94 {95 catalogVersion = 2;96 }97 // One Windows 7 this API sent version information as decimal 1 not hex (0X100 = 256)98 // so we are checking for that value as well. Reason we are not checking for version 2 above in99 // this scenario because catalog version 2 is not supported on win7.100 else if ((catalogInfo.dwPublicVersion == catalogVersion1) || (catalogInfo.dwPublicVersion == 1))101 {102 catalogVersion = 1;103 }104 else105 {106 // catalog version we don't understand107 Exception exception = new InvalidOperationException(StringUtil.Format(CatalogStrings.UnKnownCatalogVersion,108 catalogVersion1.ToString("X"),109 catalogVersion2.ToString("X")));110 111 ErrorRecord errorRecord = new ErrorRecord(exception, "UnKnownCatalogVersion", ErrorCategory.InvalidOperation, null);112 _cmdlet.ThrowTerminatingError(errorRecord);113 }114 115 return catalogVersion;116 }117 118 /// <summary>119 /// HashAlgorithm used by the Catalog. It is based on the version of Catalog.120 /// </summary>121 /// <param name="catalogVersion">Path of the output catalog file.</param>122 /// <returns>Version of the catalog.</returns>123 private static string GetCatalogHashAlgorithm(int catalogVersion)124 {125 string hashAlgorithm = string.Empty;126 127 if (catalogVersion == 1)128 {129 hashAlgorithm = HashAlgorithmSHA1;130 }131 else if (catalogVersion == 2)132 {133 hashAlgorithm = HashAlgorithmSHA256;134 }135 else136 {137 // version we don't understand138 Exception exception = new InvalidOperationException(StringUtil.Format(CatalogStrings.UnKnownCatalogVersion,139 "1.0",140 "2.0"));141 142 ErrorRecord errorRecord = new ErrorRecord(exception, "UnKnownCatalogVersion", ErrorCategory.InvalidOperation, null);143 _cmdlet.ThrowTerminatingError(errorRecord);144 }145 146 return hashAlgorithm;147 }148 149 /// <summary>150 /// Generate the Catalog Definition File representing files and folders.151 /// </summary>152 /// <param name="Path">Path of expected output .cdf file.</param>153 /// <param name="catalogFilePath">Path of the output catalog file.</param>154 /// <param name="cdfFilePath">Path of the catalog definition file.</param>155 /// <param name="catalogVersion">Version of catalog.</param>156 /// <param name="hashAlgorithm">Hash method used to generate hashes for the Catalog.</param>157 /// <returns>HashSet for the relative Path for files in Catalog.</returns>158 internal static string GenerateCDFFile(Collection<string> Path, string catalogFilePath, string cdfFilePath, int catalogVersion, string hashAlgorithm)159 {160 HashSet<string> relativePaths = new HashSet<string>();161 162 string cdfHeaderContent = string.Empty;163 string cdfFilesContent = string.Empty;164 int catAttributeCount = 0;165 166 // First create header and files section for the catalog then write in file167 cdfHeaderContent += "[CatalogHeader]" + Environment.NewLine;168 cdfHeaderContent += @"Name=" + catalogFilePath + Environment.NewLine;169 cdfHeaderContent += "CatalogVersion=" + catalogVersion + Environment.NewLine;170 cdfHeaderContent += "HashAlgorithms=" + hashAlgorithm + Environment.NewLine;171 cdfFilesContent += "[CatalogFiles]" + Environment.NewLine;172 173 foreach (string catalogFile in Path)174 {175 if (System.IO.Directory.Exists(catalogFile))176 {177 var directoryItems = Directory.EnumerateFiles(catalogFile, "*.*", SearchOption.AllDirectories);178 foreach (string fileItem in directoryItems)179 {180 ProcessFileToBeAddedInCatalogDefinitionFile(new FileInfo(fileItem), new DirectoryInfo(catalogFile), ref relativePaths, ref cdfHeaderContent, ref cdfFilesContent, ref catAttributeCount);181 }182 }183 else if (System.IO.File.Exists(catalogFile))184 {185 ProcessFileToBeAddedInCatalogDefinitionFile(new FileInfo(catalogFile), null, ref relativePaths, ref cdfHeaderContent, ref cdfFilesContent, ref catAttributeCount);186 }187 }188 189 using (System.IO.StreamWriter fileWriter = new System.IO.StreamWriter(new FileStream(cdfFilePath, FileMode.Create)))190 {191 fileWriter.WriteLine(cdfHeaderContent);192 fileWriter.WriteLine();193 fileWriter.WriteLine(cdfFilesContent);194 }195 196 return cdfFilePath;197 }198 199 /// <summary>200 /// Get file attribute (Relative path in our case) from catalog.201 /// </summary>202 /// <param name="fileToHash">File to hash.</param>203 /// <param name="dirInfo">Directory information about file needed to calculate relative file path.</param>204 /// <param name="relativePaths">Working set of relative paths of all files.</param>205 /// <param name="cdfHeaderContent">Content to be added in CatalogHeader section of cdf File.</param>206 /// <param name="cdfFilesContent">Content to be added in CatalogFiles section of cdf File.</param>207 /// <param name="catAttributeCount">Indicating the current no of catalog header level attributes.</param>208 /// <returns>Void.</returns>209 internal static void ProcessFileToBeAddedInCatalogDefinitionFile(FileInfo fileToHash, DirectoryInfo dirInfo, ref HashSet<string> relativePaths, ref string cdfHeaderContent, ref string cdfFilesContent, ref int catAttributeCount)210 {211 string relativePath = string.Empty;212 213 if (dirInfo != null)214 {215 // Relative path of the file is the path inside the containing folder excluding folder Name216 relativePath = fileToHash.FullName.AsSpan(dirInfo.FullName.Length).TrimStart('\\').ToString();217 }218 else219 {220 relativePath = fileToHash.Name;221 }222 223 if (relativePaths.Add(relativePath))224 {225 if (fileToHash.Length != 0)226 {227 cdfFilesContent += "<HASH>" + fileToHash.FullName + "=" + fileToHash.FullName + Environment.NewLine;228 cdfFilesContent += "<HASH>" + fileToHash.FullName + "ATTR1=0x10010001:FilePath:" + relativePath + Environment.NewLine;229 }230 else231 {232 // zero length files are added as catalog level attributes because they can not be hashed233 cdfHeaderContent += "CATATTR" + (++catAttributeCount) + "=0x10010001:FilePath:" + relativePath + Environment.NewLine;234 }235 }236 else237 {238 // If Files have same relative paths we can not distinguish them for239 // Validation. So failing.240 ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.FoundDuplicateFilesRelativePath, relativePath)), "FoundDuplicateFilesRelativePath", ErrorCategory.InvalidOperation, null);241 _cmdlet.ThrowTerminatingError(errorRecord);242 }243 }244 /// <summary>245 /// Generate the Catalog file for Input Catalog Definition File.246 /// </summary>247 /// <param name="cdfFilePath">Path to the Input .cdf file.</param>248 internal static void GenerateCatalogFile(string cdfFilePath)249 {250 // Open CDF File251 SafeCATCDFHandle resultCDF;252 try253 {254 resultCDF = WinTrustMethods.CryptCATCDFOpen(cdfFilePath, ParseErrorCallback);255 }256 catch (Win32Exception e)257 {258 // If we are not able to open CDF file we can not continue generating catalog259 ErrorRecord errorRecord = new ErrorRecord(260 new InvalidOperationException(CatalogStrings.UnableToOpenCatalogDefinitionFile, e),261 "UnableToOpenCatalogDefinitionFile",262 ErrorCategory.InvalidOperation,263 null);264 _cmdlet.ThrowTerminatingError(errorRecord);265 return;266 }267 268 // navigate CDF header and files sections269 using (resultCDF)270 {271 // First navigate all catalog level attributes entries first, they represent zero size files272 IntPtr catalogAttr = IntPtr.Zero;273 do274 {275 catalogAttr = WinTrustMethods.CryptCATCDFEnumCatAttributes(resultCDF, catalogAttr, ParseErrorCallback);276 277 if (catalogAttr != IntPtr.Zero)278 {279 string filePath = ProcessFilePathAttributeInCatalog(catalogAttr);280 _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.AddFileToCatalog, filePath, filePath));281 }282 } while (catalogAttr != IntPtr.Zero);283 284 // navigate all the files hash entries in the .cdf file285 IntPtr memberInfo = IntPtr.Zero;286 IntPtr memberFile = IntPtr.Zero;287 string fileName = string.Empty;288 do289 {290 memberFile = WinTrustMethods.CryptCATCDFEnumMembersByCDFTagEx(resultCDF, memberFile, ParseErrorCallback, ref memberInfo,291 fContinueOnError: true, pvReserved: IntPtr.Zero);292 fileName = Marshal.PtrToStringUni(memberFile);293 294 if (!string.IsNullOrEmpty(fileName))295 {296 IntPtr memberAttr = IntPtr.Zero;297 string fileRelativePath = string.Empty;298 do299 {300 memberAttr = WinTrustMethods.CryptCATCDFEnumAttributesWithCDFTag(resultCDF, memberFile, memberInfo, memberAttr, ParseErrorCallback);301 302 if (memberAttr != IntPtr.Zero)303 {304 fileRelativePath = ProcessFilePathAttributeInCatalog(memberAttr);305 if (!string.IsNullOrEmpty(fileRelativePath))306 {307 // Found the attribute we are looking for308 // Filename we read from the above API has <Hash> appended to its name as per CDF file tags convention309 // Truncating that Information from the string.310 string itemName = fileName.Substring(6);311 _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.AddFileToCatalog, itemName, fileRelativePath));312 break;313 }314 }315 } while (memberAttr != IntPtr.Zero);316 }317 } while (fileName != null);318 }319 }320 321 /// <summary>322 /// To generate Catalog for the folder.323 /// </summary>324 /// <param name="Path">Path to folder or File.</param>325 /// <param name="catalogFilePath">Catalog File Path.</param>326 /// <param name="catalogVersion">Catalog File Path.</param>327 /// <param name="cmdlet">Instance of cmdlet calling this method.</param>328 /// <returns>True if able to generate .cat file or false.</returns>329 internal static FileInfo GenerateCatalog(PSCmdlet cmdlet, Collection<string> Path, string catalogFilePath, int catalogVersion)330 {331 _cmdlet = cmdlet;332 string hashAlgorithm = GetCatalogHashAlgorithm(catalogVersion);333 334 if (!string.IsNullOrEmpty(hashAlgorithm))335 {336 // Generate Path for Catalog Definition File337 string cdfFilePath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetRandomFileName());338 cdfFilePath += ".cdf";339 try340 {341 cdfFilePath = GenerateCDFFile(Path, catalogFilePath, cdfFilePath, catalogVersion, hashAlgorithm);342 343 if (!File.Exists(cdfFilePath))344 {345 // If we are not able to generate catalog definition file we can not continue generating catalog346 // throw PSTraceSource.NewInvalidOperationException("catalog", CatalogStrings.CatalogDefinitionFileNotGenerated);347 ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(CatalogStrings.CatalogDefinitionFileNotGenerated), "CatalogDefinitionFileNotGenerated", ErrorCategory.InvalidOperation, null);348 _cmdlet.ThrowTerminatingError(errorRecord);349 }350 351 GenerateCatalogFile(cdfFilePath);352 if (File.Exists(catalogFilePath))353 {354 return new FileInfo(catalogFilePath);355 }356 }357 finally358 {359 File.Delete(cdfFilePath);360 }361 }362 363 return null;364 }365 366 /// <summary>367 /// Get file attribute (Relative path in our case) from catalog.368 /// </summary>369 /// <param name="memberAttrInfo">Pointer to current attribute of catalog member.</param>370 /// <returns>Value of the attribute.</returns>371 internal static string ProcessFilePathAttributeInCatalog(IntPtr memberAttrInfo)372 {373 string relativePath = string.Empty;374 375 WinTrustMethods.CRYPTCATATTRIBUTE currentMemberAttr = Marshal.PtrToStructure<WinTrustMethods.CRYPTCATATTRIBUTE>(memberAttrInfo);376 377 // check if this is the attribute we are looking for378 // catalog generated other way not using New-FileCatalog can have attributes we don't understand379 if (currentMemberAttr.pwszReferenceTag.Equals("FilePath", StringComparison.OrdinalIgnoreCase))380 {381 // find the size for the current attribute value and then allocate buffer and copy from byte array382 int attrValueSize = (int)currentMemberAttr.cbValue;383 byte[] attrValue = new byte[attrValueSize];384 Marshal.Copy(currentMemberAttr.pbValue, attrValue, 0, attrValueSize);385 relativePath = System.Text.Encoding.Unicode.GetString(attrValue);386 relativePath = relativePath.TrimEnd('\0');387 }388 389 return relativePath;390 }391 392 /// <summary>393 /// Make a hash for the file.394 /// </summary>395 /// <param name="filePath">Path of the file.</param>396 /// <param name="hashAlgorithm">Used to calculate Hash.</param>397 /// <returns>HashValue for the file.</returns>398 internal static string CalculateFileHash(string filePath, string hashAlgorithm)399 {400 string hashValue = string.Empty;401 402 // To get handle to the hash algorithm to be used to calculate hashes403 SafeCATAdminHandle catAdmin;404 try405 {406 catAdmin = WinTrustMethods.CryptCATAdminAcquireContext2(hashAlgorithm);407 }408 catch (Win32Exception e)409 {410 ErrorRecord errorRecord = new ErrorRecord(411 new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToAcquireHashAlgorithmContext, hashAlgorithm), e),412 "UnableToAcquireHashAlgorithmContext",413 ErrorCategory.InvalidOperation,414 null);415 _cmdlet.ThrowTerminatingError(errorRecord);416 417 // The method returns an empty string on a failure.418 return hashValue;419 }420 421 // Open the file that is to be hashed for reading and get its handle422 FileStream fileStream;423 try424 {425 fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);426 }427 catch (Exception e)428 {429 // If we are not able to open file that is to be hashed we can not continue with catalog validation430 ErrorRecord errorRecord = new ErrorRecord(431 new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToReadFileToHash, filePath), e),432 "UnableToReadFileToHash",433 ErrorCategory.InvalidOperation,434 null);435 _cmdlet.ThrowTerminatingError(errorRecord);436 437 // The method returns an empty string on a failure.438 return hashValue;439 }440 441 using (catAdmin)442 using (fileStream)443 {444 byte[] hashBytes = Array.Empty<byte>();445 try446 {447 hashBytes = WinTrustMethods.CryptCATAdminCalcHashFromFileHandle2(catAdmin, fileStream.SafeFileHandle);448 }449 catch (Win32Exception e)450 {451 ErrorRecord errorRecord = new ErrorRecord(452 new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToCreateFileHash, filePath), e),453 "UnableToCreateFileHash",454 ErrorCategory.InvalidOperation,455 null);456 _cmdlet.ThrowTerminatingError(errorRecord);457 }458 459 hashValue = Convert.ToHexString(hashBytes);460 }461 462 return hashValue;463 }464 465 /// <summary>466 /// Make list of hashes for given Catalog File.467 /// </summary>468 /// <param name="catalogFilePath">Path to the folder having catalog file.</param>469 /// <param name="excludedPatterns"></param>470 /// <param name="catalogVersion">The version of input catalog we read from catalog meta data after opening it.</param>471 /// <returns>Dictionary mapping files relative paths to HashValues.</returns>472 internal static Dictionary<string, string> GetHashesFromCatalog(string catalogFilePath, WildcardPattern[] excludedPatterns, out int catalogVersion)473 {474 Dictionary<string, string> catalogHashes = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);475 catalogVersion = 0;476 477 SafeCATHandle resultCatalog;478 try479 {480 resultCatalog = WinTrustMethods.CryptCATOpen(catalogFilePath, 0, IntPtr.Zero, 1, 0);481 }482 catch (Win32Exception e)483 {484 ErrorRecord errorRecord = new ErrorRecord(485 new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToOpenCatalogFile, catalogFilePath), e),486 "UnableToOpenCatalogFile",487 ErrorCategory.InvalidOperation,488 null);489 _cmdlet.ThrowTerminatingError(errorRecord);490 return catalogHashes;491 }492 493 using (resultCatalog)494 {495 IntPtr catAttrInfo = IntPtr.Zero;496 497 // First traverse all catalog level attributes to get information about zero size file.498 do499 {500 catAttrInfo = WinTrustMethods.CryptCATEnumerateCatAttr(resultCatalog, catAttrInfo);501 502 // If we found attribute it is a file information retrieve its relative path503 // and add it to catalog hash collection if its not in excluded files criteria504 if (catAttrInfo != IntPtr.Zero)505 {506 string relativePath = ProcessFilePathAttributeInCatalog(catAttrInfo);507 if (!string.IsNullOrEmpty(relativePath))508 {509 ProcessCatalogFile(relativePath, string.Empty, excludedPatterns, ref catalogHashes);510 }511 }512 } while (catAttrInfo != IntPtr.Zero);513 514 catalogVersion = GetCatalogVersion(resultCatalog);515 516 IntPtr memberInfo = IntPtr.Zero;517 // Next Navigate all members in Catalog files and get their relative paths and hashes518 do519 {520 memberInfo = WinTrustMethods.CryptCATEnumerateMember(resultCatalog, memberInfo);521 if (memberInfo != IntPtr.Zero)522 {523 WinTrustMethods.CRYPTCATMEMBER currentMember = Marshal.PtrToStructure<WinTrustMethods.CRYPTCATMEMBER>(memberInfo);524 WinTrustMethods.SIP_INDIRECT_DATA pIndirectData = Marshal.PtrToStructure<WinTrustMethods.SIP_INDIRECT_DATA>(currentMember.pIndirectData);525 526 // For Catalog version 2 CryptoAPI puts hashes of file attributes(relative path in our case) in Catalog as well527 // We validate those along with file hashes so we are skipping duplicate entries528 if (!((catalogVersion == 2) && (pIndirectData.DigestAlgorithm.pszObjId.Equals(new Oid("SHA1").Value, StringComparison.OrdinalIgnoreCase))))529 {530 string relativePath = string.Empty;531 IntPtr memberAttrInfo = IntPtr.Zero;532 do533 {534 memberAttrInfo = WinTrustMethods.CryptCATEnumerateAttr(resultCatalog, memberInfo, memberAttrInfo);535 536 if (memberAttrInfo != IntPtr.Zero)537 {538 relativePath = ProcessFilePathAttributeInCatalog(memberAttrInfo);539 if (!string.IsNullOrEmpty(relativePath))540 {541 break;542 }543 }544 }545 while (memberAttrInfo != IntPtr.Zero);546 547 // If we did not find any Relative Path for the item in catalog we should quit548 // This catalog must not be valid for our use as catalogs generated using New-FileCatalog549 // always contains relative file Paths550 if (string.IsNullOrEmpty(relativePath))551 {552 ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToOpenCatalogFile, catalogFilePath)), "UnableToOpenCatalogFile", ErrorCategory.InvalidOperation, null);553 _cmdlet.ThrowTerminatingError(errorRecord);554 }555 556 ProcessCatalogFile(relativePath, currentMember.pwszReferenceTag, excludedPatterns, ref catalogHashes);557 }558 }559 } while (memberInfo != IntPtr.Zero);560 }561 562 return catalogHashes;563 }564 565 /// <summary>566 /// Process file in path for its relative paths.567 /// </summary>568 /// <param name="relativePath">Relative path of file found in catalog.</param>569 /// <param name="fileHash">Hash of file found in catalog.</param>570 /// <param name="excludedPatterns">Skip file from validation if it matches these patterns.</param>571 /// <param name="catalogHashes">Collection of hashes of catalog.</param>572 /// <returns>Void.</returns>573 internal static void ProcessCatalogFile(string relativePath, string fileHash, WildcardPattern[] excludedPatterns, ref Dictionary<string, string> catalogHashes)574 {575 // Found the attribute we are looking for576 _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.FoundFileHashInCatalogItem, relativePath, fileHash));577 578 // Only add the file for validation if it does not meet exclusion criteria579 if (!CheckExcludedCriteria((new FileInfo(relativePath)).Name, excludedPatterns))580 {581 // Add relativePath mapping to hashvalue for each file582 catalogHashes.Add(relativePath, fileHash);583 }584 else585 {586 // Verbose about skipping file from catalog587 _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.SkipValidationOfCatalogFile, relativePath));588 }589 }590 /// <summary>591 /// Process file in path for its relative paths.592 /// </summary>593 /// <param name="fileToHash">File to hash.</param>594 /// <param name="dirInfo">Directory information about file needed to calculate relative file path.</param>595 /// <param name="hashAlgorithm">Used to calculate Hash.</param>596 /// <param name="excludedPatterns">Skip file if it matches these patterns.</param>597 /// <param name="fileHashes">Collection of hashes of files.</param>598 /// <returns>Void.</returns>599 internal static void ProcessPathFile(FileInfo fileToHash, DirectoryInfo dirInfo, string hashAlgorithm, WildcardPattern[] excludedPatterns, ref Dictionary<string, string> fileHashes)600 {601 string relativePath = string.Empty;602 string exclude = string.Empty;603 604 if (dirInfo != null)605 {606 // Relative path of the file is the path inside the containing folder excluding folder Name607 relativePath = fileToHash.FullName.AsSpan(dirInfo.FullName.Length).TrimStart('\\').ToString();608 exclude = fileToHash.Name;609 }610 else611 {612 relativePath = fileToHash.Name;613 exclude = relativePath;614 }615 616 if (!CheckExcludedCriteria(exclude, excludedPatterns))617 {618 string fileHash = string.Empty;619 620 if (fileToHash.Length != 0)621 {622 fileHash = CalculateFileHash(fileToHash.FullName, hashAlgorithm);623 }624 625 if (fileHashes.TryAdd(relativePath, fileHash))626 {627 _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.FoundFileInPath, relativePath, fileHash));628 }629 else630 {631 ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.FoundDuplicateFilesRelativePath, relativePath)), "FoundDuplicateFilesRelativePath", ErrorCategory.InvalidOperation, null);632 _cmdlet.ThrowTerminatingError(errorRecord);633 }634 }635 else636 {637 // Verbose about skipping file from path638 _cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.SkipValidationOfPathFile, relativePath));639 }640 }641 642 /// <summary>643 /// Generate the hashes of all the files in given folder.644 /// </summary>645 /// <param name="folderPaths">Path to folder or File.</param>646 /// <param name="catalogFilePath">Catalog file path it should be skipped when calculating the hashes.</param>647 /// <param name="hashAlgorithm">Used to calculate Hash.</param>648 /// <param name="excludedPatterns"></param>649 /// <returns>Dictionary mapping file relative paths to hashes..</returns>650 internal static Dictionary<string, string> CalculateHashesFromPath(Collection<string> folderPaths, string catalogFilePath, string hashAlgorithm, WildcardPattern[] excludedPatterns)651 {652 // Create a HashTable of file Hashes653 Dictionary<string, string> fileHashes = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);654 655 foreach (string folderPath in folderPaths)656 {657 if (System.IO.Directory.Exists(folderPath))658 {659 var directoryItems = Directory.EnumerateFiles(folderPath, "*.*", SearchOption.AllDirectories);660 foreach (string fileItem in directoryItems)661 {662 // if its the catalog file we are validating we will skip it663 if (string.Equals(fileItem, catalogFilePath, StringComparison.OrdinalIgnoreCase))664 continue;665 666 ProcessPathFile(new FileInfo(fileItem), new DirectoryInfo(folderPath), hashAlgorithm, excludedPatterns, ref fileHashes);667 }668 }669 else if (System.IO.File.Exists(folderPath))670 {671 ProcessPathFile(new FileInfo(folderPath), null, hashAlgorithm, excludedPatterns, ref fileHashes);672 }673 }674 675 return fileHashes;676 }677 678 /// <summary>679 /// Compare Dictionary objects.680 /// </summary>681 /// <param name="catalogItems">Hashes extracted from Catalog.</param>682 /// <param name="pathItems">Hashes created from folders path.</param>683 /// <returns>True if both collections are same.</returns>684 internal static bool CompareDictionaries(Dictionary<string, string> catalogItems, Dictionary<string, string> pathItems)685 {686 bool Status = true;687 688 List<string> relativePathsFromFolder = pathItems.Keys.ToList();689 List<string> relativePathsFromCatalog = catalogItems.Keys.ToList();690 691 // Find entries that are not in both lists. These should be empty lists for success692 // Hashes in Catalog should be exactly similar to the ones from folder693 List<string> relativePathsNotInFolder = relativePathsFromFolder.Except(relativePathsFromCatalog, StringComparer.CurrentCultureIgnoreCase).ToList();694 List<string> relativePathsNotInCatalog = relativePathsFromCatalog.Except(relativePathsFromFolder, StringComparer.CurrentCultureIgnoreCase).ToList();695 696 // Found extra hashes in Folder697 if ((relativePathsNotInFolder.Count != 0) || (relativePathsNotInCatalog.Count != 0))698 {699 Status = false;700 }701 702 foreach (KeyValuePair<string, string> item in catalogItems)703 {704 string catalogHashValue = (string)catalogItems[item.Key];705 if (pathItems.ContainsKey(item.Key))706 {707 string folderHashValue = (string)pathItems[item.Key];708 if (folderHashValue.Equals(catalogHashValue))709 {710 continue;711 }712 else713 {714 Status = false;715 }716 }717 }718 719 return Status;720 }721 /// <summary>722 /// To Validate the Integrity of Catalog.723 /// </summary>724 /// <param name="catalogFolders">Folder for which catalog is created.</param>725 /// <param name="catalogFilePath">File Name of the Catalog.</param>726 /// <param name="excludedPatterns"></param>727 /// <param name="cmdlet">Instance of cmdlet calling this method.</param>728 /// <returns>Information about Catalog.</returns>729 internal static CatalogInformation ValidateCatalog(PSCmdlet cmdlet, Collection<string> catalogFolders, string catalogFilePath, WildcardPattern[] excludedPatterns)730 {731 _cmdlet = cmdlet;732 int catalogVersion = 0;733 Dictionary<string, string> catalogHashes = GetHashesFromCatalog(catalogFilePath, excludedPatterns, out catalogVersion);734 string hashAlgorithm = GetCatalogHashAlgorithm(catalogVersion);735 736 if (!string.IsNullOrEmpty(hashAlgorithm))737 {738 Dictionary<string, string> fileHashes = CalculateHashesFromPath(catalogFolders, catalogFilePath, hashAlgorithm, excludedPatterns);739 CatalogInformation catalog = new CatalogInformation();740 catalog.CatalogItems = catalogHashes;741 catalog.PathItems = fileHashes;742 bool status = CompareDictionaries(catalogHashes, fileHashes);743 if (status)744 {745 catalog.Status = CatalogValidationStatus.Valid;746 }747 else748 {749 catalog.Status = CatalogValidationStatus.ValidationFailed;750 }751 752 catalog.HashAlgorithm = hashAlgorithm;753 catalog.Signature = SignatureHelper.GetSignature(catalogFilePath, null);754 return catalog;755 }756 757 return null;758 }759 760 /// <summary>761 /// Check if file meets the skip validation criteria.762 /// </summary>763 /// <param name="filename"></param>764 /// <param name="excludedPatterns"></param>765 /// <returns>True if match is found else false.</returns>766 internal static bool CheckExcludedCriteria(string filename, WildcardPattern[] excludedPatterns)767 {768 if (excludedPatterns != null)769 {770 foreach (WildcardPattern patternItem in excludedPatterns)771 {772 if (patternItem.IsMatch(filename))773 {774 return true;775 }776 }777 }778 779 return false;780 }781 /// <summary>782 /// Call back when error is thrown by catalog API's.783 /// </summary>784 private static void ParseErrorCallback(uint dwErrorArea, uint dwLocalError, string pwszLine)785 {786 switch (dwErrorArea)787 {788 case NativeConstants.CRYPTCAT_E_AREA_HEADER:789 break;790 case NativeConstants.CRYPTCAT_E_AREA_MEMBER:791 break;792 case NativeConstants.CRYPTCAT_E_AREA_ATTRIBUTE:793 break;794 default:795 break;796 }797 798 switch (dwLocalError)799 {800 case NativeConstants.CRYPTCAT_E_CDF_MEMBER_FILE_PATH:801 {802 ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToFindFileNameOrPathForCatalogMember, pwszLine)), "UnableToFindFileNameOrPathForCatalogMember", ErrorCategory.InvalidOperation, null);803 _cmdlet.ThrowTerminatingError(errorRecord);804 break;805 }806 case NativeConstants.CRYPTCAT_E_CDF_MEMBER_INDIRECTDATA:807 {808 ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToCreateFileHash, pwszLine)), "UnableToCreateFileHash", ErrorCategory.InvalidOperation, null);809 _cmdlet.ThrowTerminatingError(errorRecord);810 break;811 }812 case NativeConstants.CRYPTCAT_E_CDF_MEMBER_FILENOTFOUND:813 {814 ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToFindFileToHash, pwszLine)), "UnableToFindFileToHash", ErrorCategory.InvalidOperation, null);815 _cmdlet.ThrowTerminatingError(errorRecord);816 break;817 }818 case NativeConstants.CRYPTCAT_E_CDF_BAD_GUID_CONV:819 break;820 case NativeConstants.CRYPTCAT_E_CDF_ATTR_TYPECOMBO:821 break;822 case NativeConstants.CRYPTCAT_E_CDF_ATTR_TOOFEWVALUES:823 break;824 case NativeConstants.CRYPTCAT_E_CDF_UNSUPPORTED:825 break;826 case NativeConstants.CRYPTCAT_E_CDF_DUPLICATE:827 {828 ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.FoundDuplicateFileMemberInCatalog, pwszLine)), "FoundDuplicateFileMemberInCatalog", ErrorCategory.InvalidOperation, null);829 _cmdlet.ThrowTerminatingError(errorRecord);830 break;831 }832 case NativeConstants.CRYPTCAT_E_CDF_TAGNOTFOUND:833 break;834 default:835 break;836 }837 }838 }839}840 841#endif842 