/// <summary> /// Verify the signatures of a set of files. /// </summary> /// <param name="files">A set of files to verify.</param> /// <returns>An IEnumerable containing the verification results of each file.</returns> public IEnumerable <SignatureVerificationResult> VerifyFiles(IEnumerable <string> files) { foreach (string file in files) { // If the file is excluded add a default result if (Exclusions.IsExcluded(file, parent: null, containerPath: null)) { var result = SignatureVerificationResult.ExcludedFileResult(file, parent: null); Results.Add(result); } else { FileVerifier fileVerifier = GetFileVerifier(file); SignatureVerificationResult result = fileVerifier.VerifySignature(file, parent: null); if ((Options & SignatureVerificationOptions.GenerateExclusion) == SignatureVerificationOptions.GenerateExclusion) { result.ExclusionEntry = String.Join(";", String.Join("|", file, String.Empty), String.Empty, String.Empty); Log.WriteMessage(LogVerbosity.Diagnostic, SignCheckResources.DiagGenerateExclusion, result.Filename, result.ExclusionEntry); } Results.Add(result); } } return(Results); }
/// <summary> /// Verify the signature of a single file. /// </summary> /// <param name="path">The path of the file on disk to verify.</param> /// <param name="parent">The name of parent container, e.g. an MSI or VSIX. Can be null when there is no parent container.</param> /// <param name="containerPath">The path of the file in the container. This may differ from the path on disk as containers are flattened. It's /// primarily intended to help with exclusions and report more readable names.</param> /// <returns>The verification result.</returns> public SignatureVerificationResult VerifyFile(string path, string parent, string containerPath) { Log.WriteMessage(LogVerbosity.Detailed, String.Format(SignCheckResources.ProcessingFile, Path.GetFileName(path), String.IsNullOrEmpty(parent) ? SignCheckResources.NA : parent)); SignatureVerificationResult svr; if (Exclusions.IsExcluded(path, parent, containerPath)) { svr = SignatureVerificationResult.ExcludedFileResult(path, parent); } else { FileVerifier fileVerifier = GetFileVerifier(path); svr = fileVerifier.VerifySignature(path, parent); } if (GenerateExclusion) { svr.ExclusionEntry = String.Join(";", String.Join("|", path, containerPath), parent, String.Empty); Log.WriteMessage(LogVerbosity.Diagnostic, SignCheckResources.DiagGenerateExclusion, svr.Filename, svr.ExclusionEntry); } // Include the full path for top-level files if (String.IsNullOrEmpty(parent)) { svr.AddDetail(DetailKeys.File, SignCheckResources.DetailFullName, svr.FullPath); } return(svr); }
/// <summary> /// Adds a new FileVerifier to the manager that can be used to validate a file based on /// </summary> /// <param name="fileVerifier">The FileVerifier to add.</param> public static void AddFileVerifier(FileVerifier fileVerifier) { if (fileVerifier == null) { throw new ArgumentNullException("fileVerifier"); } // Let the dictionary throw if we have a duplicate file extension _fileVerifiers.Add(fileVerifier.FileExtension, fileVerifier); }
/// <summary> /// Verify the signatures of a set of files. /// </summary> /// <param name="files">A set of files to verify.</param> /// <returns>An IEnumerable containing the verification results of each file.</returns> public IEnumerable <SignatureVerificationResult> VerifyFiles(IEnumerable <string> files) { foreach (string file in files) { FileVerifier fileVerifier = GetFileVerifier(file); Results.Add(fileVerifier.VerifySignature(file, parent: null)); } return(Results); }
/// <summary> /// Retrieves a FileVerifier for the specified file. If the file's extension is unknown /// a FileVerifier can be assigned based on the file's header. /// </summary> /// <param name="path">The path of the file.</param> /// <returns>A FileVerifier that can verify the file or null if verifier could be found.</returns> public static FileVerifier GetFileVerifier(string path) { string extension = Path.GetExtension(path); FileVerifier fileVerifier = GetFileVerifierByExtension(extension); if (fileVerifier == null) { fileVerifier = GetFileVerifierByHeader(path); if (fileVerifier == null) { return(_unsupportedFileVerifier); } } return(fileVerifier); }
/// <summary> /// Verify the signature of a single file. /// </summary> /// <param name="path">The path of the file on disk to verify.</param> /// <param name="parent">The name of parent container, e.g. an MSI or VSIX. Can be null when there is no parent container.</param> /// <param name="containerPath">The path of the file in the container. This may differ from the path on disk as containers are flattened. It's /// primarily intended to help with exclusions and report more readable names.</param> /// <returns>The verification result.</returns> public SignatureVerificationResult VerifyFile(string path, string parent, string virtualPath, string containerPath) { Log.WriteMessage(LogVerbosity.Detailed, String.Format(SignCheckResources.ProcessingFile, Path.GetFileName(path), String.IsNullOrEmpty(parent) ? SignCheckResources.NA : parent)); FileVerifier fileVerifier = GetFileVerifier(path); SignatureVerificationResult svr = fileVerifier.VerifySignature(path, parent, virtualPath); svr.IsDoNotSign = Exclusions.IsDoNotSign(path, parent, virtualPath, containerPath); if ((svr.IsDoNotSign) && (svr.IsSigned)) { // Report errors if a DO-NOT-SIGN file is signed. svr.AddDetail(DetailKeys.Error, SignCheckResources.DetailDoNotSignFileSigned, svr.Filename); } if ((!svr.IsDoNotSign) && (!svr.IsSigned)) { svr.IsExcluded = Exclusions.IsExcluded(path, parent, svr.VirtualPath, containerPath); if ((svr.IsExcluded)) { svr.AddDetail(DetailKeys.File, SignCheckResources.DetailExcluded); } } if (GenerateExclusion) { svr.ExclusionEntry = String.Join(";", String.Join("|", path, containerPath), parent, String.Empty); Log.WriteMessage(LogVerbosity.Diagnostic, SignCheckResources.DiagGenerateExclusion, svr.Filename, svr.ExclusionEntry); } // Include the full path for top-level files if (String.IsNullOrEmpty(parent)) { svr.AddDetail(DetailKeys.File, SignCheckResources.DetailFullName, svr.FullPath); } if (!String.IsNullOrEmpty(virtualPath)) { svr.AddDetail(DetailKeys.File, SignCheckResources.DetailVirtualPath, svr.VirtualPath); } return(svr); }
/// <summary> /// Retrieve a FileVerifier from the SignatureVerificationManager. The retrieval is based on the file extension. If there are no /// applicable verifiers, the verifier is assigned based on the file's header. /// </summary> /// <param name="path">The path of the file for which to retrieve a FileVerifier.</param> /// <returns>A FileVerifier that can be used to verify the signature of the specified file.</returns> protected FileVerifier GetFileVerifier(string path) { string extension = Path.GetExtension(path); FileVerifier fileVerifier = SignatureVerificationManager.GetFileVerifierByExtension(extension); if (fileVerifier == null) { fileVerifier = SignatureVerificationManager.GetFileVerifierByHeader(path); if (fileVerifier == null) { return(SignatureVerificationManager.UnsupportedFileVerifier); } else { Log.WriteMessage(LogVerbosity.Detailed, SignCheckResources.DetailIdentifyByHeaderAsType, fileVerifier.FileExtension); } } return(fileVerifier); }
/// <summary> /// Verify the signature of a single file. /// </summary> /// <param name="path">The path of the file on disk to verify.</param> /// <param name="parent">The name of parent container, e.g. an MSI or VSIX. Can be null when there is no parent container.</param> /// <param name="containerPath">The path of the file in the container. This may differ from the path on disk as containers are flattened. It's /// primarily intended to help with exclusions and report more readable names.</param> /// <returns>The verification result.</returns> public SignatureVerificationResult VerifyFile(string path, string parent, string containerPath) { Log.WriteMessage(LogVerbosity.Detailed, String.Format(SignCheckResources.ProcessingFile, Path.GetFileName(path), String.IsNullOrEmpty(parent) ? SignCheckResources.NA : parent)); string extension = Path.GetExtension(path); FileVerifier fileVerifier = GetFileVerifier(path); SignatureVerificationResult svr; svr = fileVerifier.VerifySignature(path, parent); CheckAndUpdateExclusions(ref svr, path, parent, containerPath); // Include the full path for top-level files if (String.IsNullOrEmpty(parent)) { svr.AddDetail(DetailKeys.File, SignCheckResources.DetailFullName, svr.FullPath); } return(svr); }
/// <summary> /// Verify the signatures of a set of files. /// </summary> /// <param name="files">A set of files to verify.</param> /// <returns>An IEnumerable containing the verification results of each file.</returns> public IEnumerable <SignatureVerificationResult> VerifyFiles(IEnumerable <string> files) { foreach (string file in files) { FileVerifier fileVerifier = GetFileVerifier(file); SignatureVerificationResult result; result = fileVerifier.VerifySignature(file, parent: null); if ((Options & SignatureVerificationOptions.GenerateExclusion) == SignatureVerificationOptions.GenerateExclusion) { result.ExclusionEntry = String.Join(";", String.Join("|", file, String.Empty), String.Empty, String.Empty); Log.WriteMessage(LogVerbosity.Diagnostic, SignCheckResources.DiagGenerateExclusion, result.Filename, result.ExclusionEntry); } result.IsDoNotSign = Exclusions.IsDoNotSign(file, parent: null, containerPath: null); if ((result.IsDoNotSign) && (result.IsSigned)) { // Report errors if a DO-NOT-SIGN file is signed. result.AddDetail(DetailKeys.Error, SignCheckResources.DetailDoNotSignFileSigned, result.Filename); } if ((!result.IsDoNotSign) && (!result.IsSigned)) { result.IsExcluded = Exclusions.IsExcluded(file, parent: null, containerPath: null); if ((result.IsExcluded)) { result.AddDetail(DetailKeys.File, SignCheckResources.DetailExcluded); } } Results.Add(result); } return(Results); }
/// <summary> /// Retrieves a FileVerifier by looking at the header of the file to determine its type to assign an extension to it. /// </summary> /// <param name="path">The path of the file.</param> /// <returns>A FileVerifier that can verify the file or null if the verifier could not be found.</returns> public static FileVerifier GetFileVerifierByHeader(string path) { if (String.IsNullOrEmpty(path)) { throw new ArgumentException(String.Format(SignCheckResources.ArgumentNullOrEmpty, "path")); } FileVerifier fileVerifier = null; using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) using (var reader = new BinaryReader(stream)) { // Test for 4-byte magic numbers if (stream.Length > 4) { uint magic4 = reader.ReadUInt32(); if (magic4 == FileHeaders.Zip) { using (ZipArchive zipArchive = ZipFile.OpenRead(path)) { if (zipArchive.Entries.Any(z => String.Equals(Path.GetExtension(z.FullName), "nuspec", StringComparison.OrdinalIgnoreCase))) { // NUPKGs use .zip format, but should have a .nuspec files inside fileVerifier = GetFileVerifierByExtension(".nupkg"); } else if (zipArchive.Entries.Any(z => String.Equals(Path.GetExtension(z.FullName), "vsixmanifest", StringComparison.OrdinalIgnoreCase))) { // If it's an SDK based VSIX there should be a vsixmanifest file fileVerifier = GetFileVerifierByExtension(".vsix"); } else if (zipArchive.Entries.Any(z => String.Equals(z.FullName, "META-INF/MANIFEST.MF", StringComparison.OrdinalIgnoreCase))) { // Zip file with META-INF/MANIFEST.MF file is likely a JAR fileVerifier = GetFileVerifierByExtension(".jar"); } else { fileVerifier = GetFileVerifierByExtension(".zip"); } } } else if (magic4 == FileHeaders.Cab) { fileVerifier = GetFileVerifierByExtension(".cab"); } } reader.BaseStream.Seek(0, SeekOrigin.Begin); if (stream.Length > 2) { UInt16 magic2 = reader.ReadUInt16(); if (magic2 == FileHeaders.Dos) { PortableExecutableHeader pe = new PortableExecutableHeader(path); if ((pe.FileHeader.Characteristics & ImageFileCharacteristics.IMAGE_FILE_DLL) != 0) { fileVerifier = GetFileVerifierByExtension(".dll"); } else if ((pe.FileHeader.Characteristics & ImageFileCharacteristics.IMAGE_FILE_EXECUTABLE_IMAGE) != 0) { fileVerifier = GetFileVerifierByExtension(".exe"); } } } } return(fileVerifier); }