public void VerifyStrongName(SignatureVerificationResult svr, PortableExecutableHeader portableExecutableHeader) { if (portableExecutableHeader.IsManagedCode) { svr.IsNativeImage = !portableExecutableHeader.IsILImage; // NGEN/CrossGen don't preserve StrongName signatures. if (!svr.IsNativeImage) { bool wasVerified = false; int hresult = StrongName.ClrStrongName.StrongNameSignatureVerificationEx(svr.FullPath, fForceVerification: true, pfWasVerified: out wasVerified); svr.IsStrongNameSigned = hresult == StrongName.S_OK; svr.AddDetail(DetailKeys.StrongName, SignCheckResources.DetailSignedStrongName, svr.IsStrongNameSigned); if (hresult != StrongName.S_OK) { svr.AddDetail(DetailKeys.StrongName, SignCheckResources.DetailHResult, hresult); } else { string publicToken; hresult = StrongName.GetStrongNameTokenFromAssembly(svr.FullPath, out publicToken); if (hresult == StrongName.S_OK) { svr.AddDetail(DetailKeys.StrongName, SignCheckResources.DetailPublicKeyToken, publicToken); } } } else { svr.AddDetail(DetailKeys.StrongName, SignCheckResources.DetailNativeImage); } } }
public override SignatureVerificationResult VerifySignature(string path, string parent, string virtualPath) { SignatureVerificationResult svr = base.VerifySignature(path, parent, virtualPath); if (VerifyRecursive) { // MSU is just a CAB file really Log.WriteMessage(LogVerbosity.Diagnostic, SignCheckResources.DiagExtractingFileContents, svr.TempPath); CabInfo cabInfo = new CabInfo(path); cabInfo.Unpack(svr.TempPath); foreach (string cabFile in Directory.EnumerateFiles(svr.TempPath)) { string cabFileFullName = Path.GetFullPath(cabFile); SignatureVerificationResult cabEntryResult = VerifyFile(cabFile, svr.Filename, Path.Combine(svr.VirtualPath, cabFile), cabFileFullName); // Tag the full path into the result detail cabEntryResult.AddDetail(DetailKeys.File, SignCheckResources.DetailFullName, cabFileFullName); svr.NestedResults.Add(cabEntryResult); } DeleteDirectory(svr.TempPath); } return(svr); }
public override SignatureVerificationResult VerifySignature(string path, string parent) { if (VerifyJarSignatures) { var svr = new SignatureVerificationResult(path, parent); try { JarError.ClearErrors(); var jarFile = new JarFile(path); svr.IsSigned = jarFile.IsSigned(); if (!svr.IsSigned && JarError.HasErrors()) { svr.AddDetail(DetailKeys.Error, JarError.GetLastError()); } else { foreach (Timestamp timestamp in jarFile.Timestamps) { svr.AddDetail(DetailKeys.Misc, SignCheckResources.DetailTimestamp, timestamp.SignedOn, timestamp.SignatureAlgorithm); } IEnumerable <Timestamp> invalidTimestamps = from ts in jarFile.Timestamps where !ts.IsValid select ts; foreach (Timestamp ts in invalidTimestamps) { svr.AddDetail(DetailKeys.Error, SignCheckResources.DetailTimestampOutisdeCertValidity, ts.SignedOn, ts.EffectiveDate, ts.ExpiryDate); svr.IsSigned = false; } } svr.AddDetail(DetailKeys.File, SignCheckResources.DetailSigned, svr.IsSigned); } catch (Exception e) { svr.AddDetail(DetailKeys.Error, e.Message); } return(svr); } return(SignatureVerificationResult.UnsupportedFileTypeResult(path, parent)); }
/// <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); }
protected SignatureVerificationResult VerifyAuthentiCode(string path, string parent) { var svr = new SignatureVerificationResult(path, parent); uint hresult = AuthentiCode.IsSigned(path); svr.IsAuthentiCodeSigned = hresult == 0; svr.IsSigned = svr.IsAuthentiCodeSigned; // Log non-zero HRESULTs if (hresult != 0) { string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message; svr.AddDetail(DetailKeys.Error, String.Format(SignCheckResources.ErrorHResult, hresult, errorMessage)); } // TODO: Should only check if there is a signature, even if it's invalid if (VerifyAuthenticodeTimestamps) { try { svr.Timestamps = AuthentiCode.GetTimestamps(path).ToList(); foreach (Timestamp timestamp in svr.Timestamps) { svr.AddDetail(DetailKeys.AuthentiCode, SignCheckResources.DetailTimestamp, timestamp.SignedOn, timestamp.SignatureAlgorithm); svr.IsAuthentiCodeSigned &= timestamp.IsValid; } } catch { svr.AddDetail(DetailKeys.AuthentiCode, SignCheckResources.DetailTimestampError); svr.IsSigned = false; } } else { svr.AddDetail(DetailKeys.AuthentiCode, SignCheckResources.DetailTimestampSkipped); } svr.AddDetail(DetailKeys.AuthentiCode, SignCheckResources.DetailSignedAuthentiCode, svr.IsAuthentiCodeSigned); return(svr); }
public override SignatureVerificationResult VerifySignature(string path, string parent) { SignatureVerificationResult svr = new SignatureVerificationResult(path, parent); string fullPath = svr.FullPath; svr.IsSigned = IsSigned(fullPath); svr.AddDetail(DetailKeys.File, SignCheckResources.DetailSigned, svr.IsSigned); VerifyContent(svr); return(svr); }
/// <summary> /// Creates a SignatureVerificationResult for an excluded file type or file extension. /// </summary> /// <param name="path">The path to the excluded file.</param> /// <param name="parent">The parent container of the excluded file</param> /// <returns></returns> public static SignatureVerificationResult ExcludedFileResult(string path, string parent) { var signatureVerificationResult = new SignatureVerificationResult(path, parent) { IsExcluded = true }; signatureVerificationResult.AddDetail(DetailKeys.File, SignCheckResources.DetailExcluded); return(signatureVerificationResult); }
/// <summary> /// Creates a SignatureVerificationResult for an unsupported file type or file extension. /// </summary> /// <param name="path">The path to the file that is unsupported</param> /// <returns>A SignatureVerificationResult indicating the file is unsupported..</returns> public static SignatureVerificationResult UnsupportedFileTypeResult(string path, string parent) { var signatureVerificationResult = new SignatureVerificationResult(path, parent) { IsSkipped = true }; signatureVerificationResult.AddDetail(DetailKeys.File, SignCheckResources.DetailSkippedUnsupportedFileType); return(signatureVerificationResult); }
/// <summary> /// Verify the contents of a zip-based archive and add the results to the container result. /// </summary> /// <param name="svr">The container result</param> protected void VerifyContent(SignatureVerificationResult svr) { if (VerifyRecursive) { using (ZipArchive zipArchive = ZipFile.OpenRead(svr.FullPath)) { string tempPath = svr.TempPath; CreateDirectory(tempPath); Log.WriteMessage(LogVerbosity.Diagnostic, SignCheckResources.DiagExtractingFileContents, tempPath); Dictionary <string, string> archiveMap = new Dictionary <string, string>(); foreach (ZipArchiveEntry archiveEntry in zipArchive.Entries) { // Generate an alias for the actual file that has the same extension. We do this to avoid path too long errors so that // containers can be flattened. string directoryName = Path.GetDirectoryName(archiveEntry.FullName); string hashedPath = String.IsNullOrEmpty(directoryName) ? Utils.GetHash(@".\", HashAlgorithmName.SHA256.Name) : Utils.GetHash(directoryName, HashAlgorithmName.SHA256.Name); string extension = Path.GetExtension(archiveEntry.FullName); // CAB files cannot be aliased since they're referred to from the Media table inside the MSI string aliasFileName = String.Equals(extension.ToLowerInvariant(), ".cab") ? Path.GetFileName(archiveEntry.FullName) : Utils.GetHash(archiveEntry.FullName, HashAlgorithmName.SHA256.Name) + Path.GetExtension(archiveEntry.FullName); // lgtm [cs/zipslip] Archive from trusted source string aliasFullName = Path.Combine(tempPath, hashedPath, aliasFileName); if (File.Exists(aliasFullName)) { Log.WriteMessage(LogVerbosity.Normal, SignCheckResources.FileAlreadyExists, aliasFullName); } else { CreateDirectory(Path.GetDirectoryName(aliasFullName)); archiveEntry.ExtractToFile(aliasFullName); // lgtm [cs/microsoft/zipslip] Archive from trusted source archiveMap[archiveEntry.FullName] = aliasFullName; } } // We can only verify once everything is extracted. This is mainly because MSIs can have mutliple external CAB files // and we need to ensure they are extracted before we verify the MSIs. foreach (string fullName in archiveMap.Keys) { SignatureVerificationResult result = VerifyFile(archiveMap[fullName], svr.Filename, Path.Combine(svr.VirtualPath, fullName), fullName); // Tag the full path into the result detail result.AddDetail(DetailKeys.File, SignCheckResources.DetailFullName, fullName); svr.NestedResults.Add(result); } DeleteDirectory(tempPath); } } }
public override SignatureVerificationResult VerifySignature(string path, string parent) { if (VerifyXmlSignatures) { X509Certificate2 xmlCertificate; var svr = new SignatureVerificationResult(path, parent); svr.IsSigned = IsSigned(svr.FullPath, out xmlCertificate); svr.AddDetail(DetailKeys.File, SignCheckResources.DetailSigned, svr.IsSigned); return(svr); } return(SignatureVerificationResult.UnsupportedFileTypeResult(path, parent)); }
public override SignatureVerificationResult VerifySignature(string path, string parent) { SignatureVerificationResult svr = VerifyAuthentiCode(path, parent); if (FinalizeResult) { // Derived class that need to evaluate additional properties and results must // set FinalizeResult = false, otherwise the Signed result can be logged multiple times. svr.AddDetail(DetailKeys.File, SignCheckResources.DetailSigned, svr.IsSigned); } return(svr); }
/// <summary> /// Verify whether the portable executable contains an AuthentiCode signature and optionally check the /// StrongName signature if it is enabled and the file represents a managed code executable. /// </summary> /// <param name="path"></param> /// <param name="parent"></param> /// <returns></returns> public override SignatureVerificationResult VerifySignature(string path, string parent) { // Defer to the base implementation to check the AuthentiCode signature. SignatureVerificationResult svr = base.VerifySignature(path, parent); PEHeader = new PortableExecutableHeader(svr.FullPath); if (VerifyStrongNameSignature) { VerifyStrongName(svr, PEHeader); } svr.IsSigned = svr.IsAuthentiCodeSigned & ((svr.IsStrongNameSigned) || (!VerifyStrongNameSignature) || svr.IsNativeImage); svr.AddDetail(DetailKeys.File, SignCheckResources.DetailSigned, svr.IsSigned); return(svr); }
/// <summary> /// Checks whether the specified file is excluded from verification and updates the SignatureVerificationResult. /// If <see cref="GenerateExclusion"/> is true, an exclusion entry is also generated. /// </summary> /// <param name="result">The SignatureVerificationResult to update.</param> /// <param name="path">The path of the file on disk.</param> /// <param name="parent">The parent (container) of the file.</param> /// <param name="containerPath">The path of the file in the container. May be null if the file is not embedded in a container.</param> protected void CheckAndUpdateExclusions(ref SignatureVerificationResult result, string path, string parent, string containerPath) { if (Exclusions.Count > 0) { result.IsExcluded = Exclusions.IsParentExcluded(parent) || Exclusions.IsFileExcluded(path) || Exclusions.IsFileExcluded(containerPath); if (result.IsExcluded) { result.AddDetail(DetailKeys.File, SignCheckResources.DetailExcluded); } } if (GenerateExclusion) { result.ExclusionEntry = String.Join(";", String.Join("|", path, containerPath), parent, String.Empty); Log.WriteMessage(LogVerbosity.Diagnostic, SignCheckResources.DiagGenerateExclusion, result.Filename, result.ExclusionEntry); } }
public override SignatureVerificationResult VerifySignature(string path, string parent) { var svr = SignatureVerificationResult.UnsupportedFileTypeResult(path, parent); string fullPath = svr.FullPath; svr.AddDetail(DetailKeys.File, SignCheckResources.DetailSigned, SignCheckResources.NA); if (VerifyRecursive) { using (ZipArchive zipArchive = ZipFile.OpenRead(fullPath)) { string tempPath = svr.TempPath; CreateDirectory(tempPath); Log.WriteMessage(LogVerbosity.Diagnostic, SignCheckResources.DiagExtractingFileContents, tempPath); foreach (ZipArchiveEntry archiveEntry in zipArchive.Entries) { // Generate an alias for the actual file that has the same extension. We do this to avoid path too long errors so that // containers can be flattened string aliasFileName = Utils.GetHash(archiveEntry.FullName, HashAlgorithmName.MD5.Name) + Path.GetExtension(archiveEntry.FullName); string aliasFullName = Path.Combine(tempPath, aliasFileName); if (File.Exists(aliasFullName)) { Log.WriteMessage(LogVerbosity.Normal, SignCheckResources.FileAlreadyExists, aliasFullName); } else { archiveEntry.ExtractToFile(aliasFullName); SignatureVerificationResult archiveEntryResult = VerifyFile(aliasFullName, svr.Filename, archiveEntry.FullName); // Tag the full path into the result detail archiveEntryResult.AddDetail(DetailKeys.File, SignCheckResources.DetailFullName, archiveEntry.FullName); svr.NestedResults.Add(archiveEntryResult); } } DeleteDirectory(tempPath); } } return(svr); }
/// <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)); FileVerifier fileVerifier = GetFileVerifier(path); SignatureVerificationResult svr = fileVerifier.VerifySignature(path, parent); svr.IsExcluded = Exclusions.IsExcluded(path, parent, containerPath); 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); }
public override SignatureVerificationResult VerifySignature(string path, string parent) { var svr = new SignatureVerificationResult(path, parent); string fullPath = svr.FullPath; svr.IsSigned = IsSigned(fullPath, svr); svr.AddDetail(DetailKeys.File, SignCheckResources.DetailSigned, svr.IsSigned); if (VerifyRecursive) { using (ZipArchive zipArchive = ZipFile.OpenRead(fullPath)) { string tempPath = svr.TempPath; CreateDirectory(tempPath); foreach (ZipArchiveEntry archiveEntry in zipArchive.Entries) { // Generate an alias for the actual file, but keep the original extension. This should limit the chances of running // into 'path too long' errors when extracting the files. string aliasFileName = Utils.GetHash(archiveEntry.FullName, HashAlgorithmName.MD5.Name) + Path.GetExtension(archiveEntry.FullName); string aliasFullName = Path.Combine(tempPath, aliasFileName); archiveEntry.ExtractToFile(aliasFullName); SignatureVerificationResult archiveEntryResult = VerifyFile(aliasFullName, svr.Filename, archiveEntry.FullName); // Tag the full path into the result detail archiveEntryResult.AddDetail(DetailKeys.File, SignCheckResources.DetailFullName, archiveEntry.FullName); svr.NestedResults.Add(archiveEntryResult); } DeleteDirectory(tempPath); } } return(svr); }
private bool IsSigned(string path, SignatureVerificationResult result) { PackageDigitalSignature packageSignature = null; using (var vsixStream = new FileStream(path, FileMode.Open, FileAccess.Read)) { var vsixPackage = Package.Open(vsixStream); var signatureManager = new PackageDigitalSignatureManager(vsixPackage); if (!signatureManager.IsSigned) { return(false); } if (signatureManager.Signatures.Count() != 1) { return(false); } if (signatureManager.Signatures[0].SignedParts.Count != vsixPackage.GetParts().Count() - 1) { return(false); } packageSignature = signatureManager.Signatures[0]; // Retrieve the timestamp Timestamp timestamp; if (!TryGetTimestamp(packageSignature, out timestamp)) { // Timestamp is either invalid or not present result.AddDetail(DetailKeys.Error, SignCheckResources.ErrorInvalidOrMissingTimestamp); return(false); } // Update the result with the timestamp detail result.AddDetail(DetailKeys.Signature, String.Format(SignCheckResources.DetailTimestamp, timestamp.SignedOn, timestamp.SignatureAlgorithm)); // Verify the certificate chain X509Certificate2 certificate = new X509Certificate2(packageSignature.Signer); X509Chain certChain = new X509Chain(); certChain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; certChain.ChainPolicy.RevocationMode = X509RevocationMode.Online; // If the certificate has expired, but the VSIX was signed prior to expiration // we can ignore invalid time policies. bool certExpired = DateTime.Now > certificate.NotAfter; if (timestamp.IsValid && certExpired) { certChain.ChainPolicy.VerificationFlags |= X509VerificationFlags.IgnoreNotTimeValid; } if (!certChain.Build(certificate)) { result.AddDetail(DetailKeys.Error, SignCheckResources.DetailErrorFailedToBuildCertChain); return(false); } result.AddDetail(DetailKeys.Misc, SignCheckResources.DetailCertChainValid); } return(true); }
public override SignatureVerificationResult VerifySignature(string path, string parent, string virtualPath) { SignatureVerificationResult svr = base.VerifySignature(path, parent, virtualPath); if (VerifyRecursive) { CreateDirectory(svr.TempPath); // TODO: Fix for MSIs with external CABs that are not present. using (var installPackage = new InstallPackage(svr.FullPath, DatabaseOpenMode.Transact, sourceDir: null, workingDir: svr.TempPath)) { InstallPathMap files = installPackage.Files; var originalFiles = new Dictionary <string, string>(); // Flatten the files to avoid path too long errors. We use the File column and extension to create a unique file // and record the original, relative MSI path in the result. foreach (string key in installPackage.Files.Keys) { originalFiles[key] = installPackage.Files[key].TargetPath; string name = key + Path.GetExtension(installPackage.Files[key].TargetName); string targetPath = Path.Combine(svr.TempPath, name); installPackage.Files[key].TargetName = name; installPackage.Files[key].SourceName = name; installPackage.Files[key].SourcePath = targetPath; installPackage.Files[key].TargetPath = targetPath; } try { Log.WriteMessage(LogVerbosity.Diagnostic, SignCheckResources.DiagExtractingFileContents, svr.TempPath); installPackage.ExtractFiles(installPackage.Files.Keys); foreach (string key in installPackage.Files.Keys) { SignatureVerificationResult packageFileResult = VerifyFile(installPackage.Files[key].TargetPath, svr.Filename, Path.Combine(svr.VirtualPath, originalFiles[key]), containerPath: null); packageFileResult.AddDetail(DetailKeys.File, SignCheckResources.DetailFullName, originalFiles[key]); svr.NestedResults.Add(packageFileResult); } } catch (Exception e) { Log.WriteError(e.Message); } } // Extract files from the Binary table - this is where items such as custom actions are stored. try { using (var installDatabase = new Database(svr.FullPath, DatabaseOpenMode.ReadOnly)) using (View view = installDatabase.OpenView("SELECT `Name`, `Data` FROM `Binary`")) { view.Execute(); foreach (Record record in view) { string binaryFile = (string)record["Name"]; string binaryFilePath = Path.Combine(svr.TempPath, binaryFile); StructuredStorage.SaveStream(record, svr.TempPath); SignatureVerificationResult binaryStreamResult = VerifyFile(binaryFilePath, svr.Filename, Path.Combine(svr.VirtualPath, binaryFile), containerPath: null); binaryStreamResult.AddDetail(DetailKeys.Misc, SignCheckResources.FileExtractedFromBinaryTable); svr.NestedResults.Add(binaryStreamResult); record.Close(); } } } catch (Exception e) { Log.WriteError(e.Message); } DeleteDirectory(svr.TempPath); } return(svr); }