From 1c596661b7423a2d9f49869a795caf7f8ceabd90 Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Fri, 28 May 2021 17:52:35 -0400 Subject: [PATCH 01/19] * add pkcs nuget dependency * rename checkers -> Checkers * add SignedFile verifier and use it to verify the signed file --- .gitignore | 2 +- ZitiUpdateService/UpdateService.cs | 15 +- ZitiUpdateService/ZitiUpdateService.csproj | 8 + ZitiUpdateService/checkers/FilesystemCheck.cs | 6 +- ZitiUpdateService/checkers/GithubCheck.cs | 5 +- .../checkers/PeFile/SignedFileStructs.cs | 539 ++++++++++++++++++ .../checkers/PeFile/SignedFileValidator.cs | 344 +++++++++++ .../checkers/PeFile/StructHelper.cs | 17 + ZitiUpdateService/checkers/UpdateCheckers.cs | 2 +- ZitiUpdateService/packages.config | 3 +- 10 files changed, 925 insertions(+), 16 deletions(-) create mode 100644 ZitiUpdateService/checkers/PeFile/SignedFileStructs.cs create mode 100644 ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs create mode 100644 ZitiUpdateService/checkers/PeFile/StructHelper.cs diff --git a/.gitignore b/.gitignore index 9312a38f..cfec3e2b 100644 --- a/.gitignore +++ b/.gitignore @@ -268,7 +268,7 @@ service/libuv.dll Installer/build/ Installer/ziti-tunnel-service.zip ziti-tunnel-service.zip -WinSign.p12 +WinSign.* *.p12 *.pfx *.pem diff --git a/ZitiUpdateService/UpdateService.cs b/ZitiUpdateService/UpdateService.cs index bfd6e615..59f7b246 100644 --- a/ZitiUpdateService/UpdateService.cs +++ b/ZitiUpdateService/UpdateService.cs @@ -19,6 +19,7 @@ using NLog; using Newtonsoft.Json; using System.Net; using DnsClient; +using ZitiUpdateService.Checkers.PeFile; namespace ZitiUpdateService { public partial class UpdateService : ServiceBase { @@ -54,7 +55,7 @@ namespace ZitiUpdateService { IPCServer svr = new IPCServer(); Task ipcServer = null; Task eventServer = null; - checkers.UpdateCheck check = null; + Checkers.UpdateCheck check = null; private Timer dnsProbeTimer = new Timer(); private IPAddress dnsIpAddress = null; @@ -574,9 +575,9 @@ namespace ZitiUpdateService { updateUrl = "https://api.github.com/repos/openziti/desktop-edge-win-beta/releases/latest"; } if (useGithubCheck) { - check = new checkers.GithubCheck(updateUrl); + check = new Checkers.GithubCheck(updateUrl); } else { - check = new checkers.FilesystemCheck(1); + check = new Checkers.FilesystemCheck(1); } } @@ -605,7 +606,7 @@ namespace ZitiUpdateService { Logger.Warn("Still in update check. This is abnormal. Please report if you see this warning"); return; } - inUpdateCheck = true; //simple semaphone + inUpdateCheck = true; //simple semaphore try { Logger.Debug("checking for update"); @@ -626,8 +627,8 @@ namespace ZitiUpdateService { string fileDestination = Path.Combine(updateFolder, filename); if (check.AlreadyDownloaded(updateFolder, filename)) { - Logger.Info("package has already been downloaded to {0} - moving to install phase", Path.Combine(updateFolder, filename)); - } else { + Logger.Info("package has already been downloaded to {0} - moving to install phase", fileDestination); + } else { Logger.Info("copying update package begins"); check.CopyUpdatePackage(updateFolder, filename); Logger.Info("copying update package complete"); @@ -641,6 +642,8 @@ namespace ZitiUpdateService { } Logger.Debug("downloaded file hash was correct. update can continue."); + new SignedFileValidator(fileDestination).Verify(); + // check digital signature var signer = X509Certificate.CreateFromSignedFile(fileDestination); /* keep these commented out lines - just in case we need all the certs from the file use this diff --git a/ZitiUpdateService/ZitiUpdateService.csproj b/ZitiUpdateService/ZitiUpdateService.csproj index cbb3bf6a..6f14703f 100644 --- a/ZitiUpdateService/ZitiUpdateService.csproj +++ b/ZitiUpdateService/ZitiUpdateService.csproj @@ -99,6 +99,10 @@ ..\packages\Microsoft.PowerShell.5.ReferenceAssemblies.1.1.0\lib\net4\System.Management.Automation.dll + + + ..\packages\System.Security.Cryptography.Pkcs.5.0.1\lib\net461\System.Security.Cryptography.Pkcs.dll + @@ -113,6 +117,9 @@ + + + Component @@ -170,5 +177,6 @@ ZitiDesktopEdge.Client + \ No newline at end of file diff --git a/ZitiUpdateService/checkers/FilesystemCheck.cs b/ZitiUpdateService/checkers/FilesystemCheck.cs index 6d0d456e..ef56ad32 100644 --- a/ZitiUpdateService/checkers/FilesystemCheck.cs +++ b/ZitiUpdateService/checkers/FilesystemCheck.cs @@ -1,13 +1,9 @@ using System; using System.IO; -using System.Net; - -using System.Security.Cryptography; using NLog; -using Newtonsoft.Json.Linq; -namespace ZitiUpdateService.checkers { +namespace ZitiUpdateService.Checkers { internal class FilesystemCheck : UpdateCheck { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); string dest = null; diff --git a/ZitiUpdateService/checkers/GithubCheck.cs b/ZitiUpdateService/checkers/GithubCheck.cs index eeca9452..37512d30 100644 --- a/ZitiUpdateService/checkers/GithubCheck.cs +++ b/ZitiUpdateService/checkers/GithubCheck.cs @@ -7,8 +7,9 @@ using NLog; using Newtonsoft.Json.Linq; using ZitiDesktopEdge.Utility; +using ZitiUpdateService.Checkers.PeFile; -namespace ZitiUpdateService.checkers { +namespace ZitiUpdateService.Checkers { internal class GithubCheck : UpdateCheck { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); @@ -97,7 +98,7 @@ namespace ZitiUpdateService.checkers { File.Delete(sha256dest); return computed.ToLower().Trim() == hash.ToLower().Trim(); } - } + } override public Version GetNextVersion() { return nextVersion; diff --git a/ZitiUpdateService/checkers/PeFile/SignedFileStructs.cs b/ZitiUpdateService/checkers/PeFile/SignedFileStructs.cs new file mode 100644 index 00000000..ce1d2cc9 --- /dev/null +++ b/ZitiUpdateService/checkers/PeFile/SignedFileStructs.cs @@ -0,0 +1,539 @@ +using System; +using System.Runtime.InteropServices; + +namespace ZitiUpdateService.Checkers.PeFile { + + //defined by http://www.pinvoke.net/default.aspx/Structures/IMAGE_FILE_HEADER.html + [StructLayout(LayoutKind.Sequential)] + public struct IMAGE_FILE_HEADER { + public UInt16 Machine; + public UInt16 NumberOfSections; + public UInt32 TimeDateStamp; + public UInt32 PointerToSymbolTable; + public UInt32 NumberOfSymbols; + public UInt16 SizeOfOptionalHeader; + public UInt16 Characteristics; + } + + //see http://www.pinvoke.net/default.aspx/Structures/IMAGE_OPTIONAL_HEADER32.html + //defined by http://www.pinvoke.net/default.aspx/Structures/IMAGE_OPTIONAL_HEADER32.html + [StructLayout(LayoutKind.Sequential)] + public struct IMAGE_DATA_DIRECTORY { + public UInt32 VirtualAddress; + public UInt32 Size; + } + + public enum MachineType : ushort { + Native = 0, + I386 = 0x014c, + Itanium = 0x0200, + x64 = 0x8664 + } + + public sealed class MagicType { + public const Int16 IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b; + public const Int16 IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b; + } + + public enum SubSystemType : ushort { + IMAGE_SUBSYSTEM_UNKNOWN = 0, + IMAGE_SUBSYSTEM_NATIVE = 1, + IMAGE_SUBSYSTEM_WINDOWS_GUI = 2, + IMAGE_SUBSYSTEM_WINDOWS_CUI = 3, + IMAGE_SUBSYSTEM_POSIX_CUI = 7, + IMAGE_SUBSYSTEM_WINDOWS_CE_GUI = 9, + IMAGE_SUBSYSTEM_EFI_APPLICATION = 10, + IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11, + IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER = 12, + IMAGE_SUBSYSTEM_EFI_ROM = 13, + IMAGE_SUBSYSTEM_XBOX = 14 + + } + + public enum DllCharacteristicsType : ushort { + RES_0 = 0x0001, + RES_1 = 0x0002, + RES_2 = 0x0004, + RES_3 = 0x0008, + IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040, + IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY = 0x0080, + IMAGE_DLL_CHARACTERISTICS_NX_COMPAT = 0x0100, + IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200, + IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400, + IMAGE_DLLCHARACTERISTICS_NO_BIND = 0x0800, + RES_4 = 0x1000, + IMAGE_DLLCHARACTERISTICS_WDM_DRIVER = 0x2000, + IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000 + } + + [StructLayout(LayoutKind.Explicit)] + public struct IMAGE_OPTIONAL_HEADER32 { + [FieldOffset(0)] public ushort Magic; + + [FieldOffset(2)] public byte MajorLinkerVersion; + + [FieldOffset(3)] public byte MinorLinkerVersion; + + [FieldOffset(4)] public uint SizeOfCode; + + [FieldOffset(8)] public uint SizeOfInitializedData; + + [FieldOffset(12)] public uint SizeOfUninitializedData; + + [FieldOffset(16)] public uint AddressOfEntryPoint; + + [FieldOffset(20)] public uint BaseOfCode; + + // PE32 contains this additional field + [FieldOffset(24)] public uint BaseOfData; + + [FieldOffset(28)] public uint ImageBase; + + [FieldOffset(32)] public uint SectionAlignment; + + [FieldOffset(36)] public uint FileAlignment; + + [FieldOffset(40)] public ushort MajorOperatingSystemVersion; + + [FieldOffset(42)] public ushort MinorOperatingSystemVersion; + + [FieldOffset(44)] public ushort MajorImageVersion; + + [FieldOffset(46)] public ushort MinorImageVersion; + + [FieldOffset(48)] public ushort MajorSubsystemVersion; + + [FieldOffset(50)] public ushort MinorSubsystemVersion; + + [FieldOffset(52)] public uint Win32VersionValue; + + [FieldOffset(56)] public uint SizeOfImage; + + [FieldOffset(60)] public uint SizeOfHeaders; + + [FieldOffset(64)] public uint CheckSum; + + [FieldOffset(68)] public SubSystemType Subsystem; + + [FieldOffset(70)] public DllCharacteristicsType DllCharacteristics; + + [FieldOffset(72)] public uint SizeOfStackReserve; + + [FieldOffset(76)] public uint SizeOfStackCommit; + + [FieldOffset(80)] public uint SizeOfHeapReserve; + + [FieldOffset(84)] public uint SizeOfHeapCommit; + + [FieldOffset(88)] public uint LoaderFlags; + + [FieldOffset(92)] public uint NumberOfRvaAndSizes; + + [FieldOffset(96)] public IMAGE_DATA_DIRECTORY ExportTable; + + [FieldOffset(104)] public IMAGE_DATA_DIRECTORY ImportTable; + + [FieldOffset(112)] public IMAGE_DATA_DIRECTORY ResourceTable; + + [FieldOffset(120)] public IMAGE_DATA_DIRECTORY ExceptionTable; + + [FieldOffset(128)] public IMAGE_DATA_DIRECTORY CertificateTable; + + [FieldOffset(136)] public IMAGE_DATA_DIRECTORY BaseRelocationTable; + + [FieldOffset(144)] public IMAGE_DATA_DIRECTORY Debug; + + [FieldOffset(152)] public IMAGE_DATA_DIRECTORY Architecture; + + [FieldOffset(160)] public IMAGE_DATA_DIRECTORY GlobalPtr; + + [FieldOffset(168)] public IMAGE_DATA_DIRECTORY TLSTable; + + [FieldOffset(176)] public IMAGE_DATA_DIRECTORY LoadConfigTable; + + [FieldOffset(184)] public IMAGE_DATA_DIRECTORY BoundImport; + + [FieldOffset(192)] public IMAGE_DATA_DIRECTORY IAT; + + [FieldOffset(200)] public IMAGE_DATA_DIRECTORY DelayImportDescriptor; + + [FieldOffset(208)] public IMAGE_DATA_DIRECTORY CLRRuntimeHeader; + + [FieldOffset(216)] public IMAGE_DATA_DIRECTORY Reserved; + } + + [StructLayout(LayoutKind.Explicit)] + public struct IMAGE_OPTIONAL_HEADER64 { + [FieldOffset(0)] public ushort Magic; + + [FieldOffset(2)] public byte MajorLinkerVersion; + + [FieldOffset(3)] public byte MinorLinkerVersion; + + [FieldOffset(4)] public uint SizeOfCode; + + [FieldOffset(8)] public uint SizeOfInitializedData; + + [FieldOffset(12)] public uint SizeOfUninitializedData; + + [FieldOffset(16)] public uint AddressOfEntryPoint; + + [FieldOffset(20)] public uint BaseOfCode; + + [FieldOffset(24)] public ulong ImageBase; + + [FieldOffset(32)] public uint SectionAlignment; + + [FieldOffset(36)] public uint FileAlignment; + + [FieldOffset(40)] public ushort MajorOperatingSystemVersion; + + [FieldOffset(42)] public ushort MinorOperatingSystemVersion; + + [FieldOffset(44)] public ushort MajorImageVersion; + + [FieldOffset(46)] public ushort MinorImageVersion; + + [FieldOffset(48)] public ushort MajorSubsystemVersion; + + [FieldOffset(50)] public ushort MinorSubsystemVersion; + + [FieldOffset(52)] public uint Win32VersionValue; + + [FieldOffset(56)] public uint SizeOfImage; + + [FieldOffset(60)] public uint SizeOfHeaders; + + [FieldOffset(64)] public uint CheckSum; + + [FieldOffset(68)] public SubSystemType Subsystem; + + [FieldOffset(70)] public DllCharacteristicsType DllCharacteristics; + + [FieldOffset(72)] public ulong SizeOfStackReserve; + + [FieldOffset(80)] public ulong SizeOfStackCommit; + + [FieldOffset(88)] public ulong SizeOfHeapReserve; + + [FieldOffset(96)] public ulong SizeOfHeapCommit; + + [FieldOffset(104)] public uint LoaderFlags; + + [FieldOffset(108)] public uint NumberOfRvaAndSizes; + + [FieldOffset(112)] public IMAGE_DATA_DIRECTORY ExportTable; + + [FieldOffset(120)] public IMAGE_DATA_DIRECTORY ImportTable; + + [FieldOffset(128)] public IMAGE_DATA_DIRECTORY ResourceTable; + + [FieldOffset(136)] public IMAGE_DATA_DIRECTORY ExceptionTable; + + [FieldOffset(144)] public IMAGE_DATA_DIRECTORY CertificateTable; + + [FieldOffset(152)] public IMAGE_DATA_DIRECTORY BaseRelocationTable; + + [FieldOffset(160)] public IMAGE_DATA_DIRECTORY Debug; + + [FieldOffset(168)] public IMAGE_DATA_DIRECTORY Architecture; + + [FieldOffset(176)] public IMAGE_DATA_DIRECTORY GlobalPtr; + + [FieldOffset(184)] public IMAGE_DATA_DIRECTORY TLSTable; + + [FieldOffset(192)] public IMAGE_DATA_DIRECTORY LoadConfigTable; + + [FieldOffset(200)] public IMAGE_DATA_DIRECTORY BoundImport; + + [FieldOffset(208)] public IMAGE_DATA_DIRECTORY IAT; + + [FieldOffset(216)] public IMAGE_DATA_DIRECTORY DelayImportDescriptor; + + [FieldOffset(224)] public IMAGE_DATA_DIRECTORY CLRRuntimeHeader; + + [FieldOffset(232)] public IMAGE_DATA_DIRECTORY Reserved; + } + + + [StructLayout(LayoutKind.Explicit)] + public struct IMAGE_SECTION_HEADER { + //FROM: http://www.pinvoke.net/default.aspx/Structures/IMAGE_SECTION_HEADER.html + [FieldOffset(0)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public char[] Name; + + [FieldOffset(8)] public UInt32 VirtualSize; + + [FieldOffset(12)] public UInt32 VirtualAddress; + + [FieldOffset(16)] public UInt32 SizeOfRawData; + + [FieldOffset(20)] public UInt32 PointerToRawData; + + [FieldOffset(24)] public UInt32 PointerToRelocations; + + [FieldOffset(28)] public UInt32 PointerToLinenumbers; + + [FieldOffset(32)] public UInt16 NumberOfRelocations; + + [FieldOffset(34)] public UInt16 NumberOfLinenumbers; + + [FieldOffset(36)] public DataSectionFlags Characteristics; + + public string Section { + get { return new string(Name); } + } + } + + [Flags] + public enum DataSectionFlags : uint { + //FROM: http://www.pinvoke.net/default.aspx/Structures/IMAGE_SECTION_HEADER.html + /// + /// Reserved for future use. + /// + TypeReg = 0x00000000, + + /// + /// Reserved for future use. + /// + TypeDsect = 0x00000001, + + /// + /// Reserved for future use. + /// + TypeNoLoad = 0x00000002, + + /// + /// Reserved for future use. + /// + TypeGroup = 0x00000004, + + /// + /// The section should not be padded to the next boundary. This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. This is valid only for object files. + /// + TypeNoPadded = 0x00000008, + + /// + /// Reserved for future use. + /// + TypeCopy = 0x00000010, + + /// + /// The section contains executable code. + /// + ContentCode = 0x00000020, + + /// + /// The section contains initialized data. + /// + ContentInitializedData = 0x00000040, + + /// + /// The section contains uninitialized data. + /// + ContentUninitializedData = 0x00000080, + + /// + /// Reserved for future use. + /// + LinkOther = 0x00000100, + + /// + /// The section contains comments or other information. The .drectve section has this type. This is valid for object files only. + /// + LinkInfo = 0x00000200, + + /// + /// Reserved for future use. + /// + TypeOver = 0x00000400, + + /// + /// The section will not become part of the image. This is valid only for object files. + /// + LinkRemove = 0x00000800, + + /// + /// The section contains COMDAT data. For more information, see section 5.5.6, COMDAT Sections (Object Only). This is valid only for object files. + /// + LinkComDat = 0x00001000, + + /// + /// Reset speculative exceptions handling bits in the TLB entries for this section. + /// + NoDeferSpecExceptions = 0x00004000, + + /// + /// The section contains data referenced through the global pointer (GP). + /// + RelativeGP = 0x00008000, + + /// + /// Reserved for future use. + /// + MemPurgeable = 0x00020000, + + /// + /// Reserved for future use. + /// + Memory16Bit = 0x00020000, + + /// + /// Reserved for future use. + /// + MemoryLocked = 0x00040000, + + /// + /// Reserved for future use. + /// + MemoryPreload = 0x00080000, + + /// + /// Align data on a 1-byte boundary. Valid only for object files. + /// + Align1Bytes = 0x00100000, + + /// + /// Align data on a 2-byte boundary. Valid only for object files. + /// + Align2Bytes = 0x00200000, + + /// + /// Align data on a 4-byte boundary. Valid only for object files. + /// + Align4Bytes = 0x00300000, + + /// + /// Align data on an 8-byte boundary. Valid only for object files. + /// + Align8Bytes = 0x00400000, + + /// + /// Align data on a 16-byte boundary. Valid only for object files. + /// + Align16Bytes = 0x00500000, + + /// + /// Align data on a 32-byte boundary. Valid only for object files. + /// + Align32Bytes = 0x00600000, + + /// + /// Align data on a 64-byte boundary. Valid only for object files. + /// + Align64Bytes = 0x00700000, + + /// + /// Align data on a 128-byte boundary. Valid only for object files. + /// + Align128Bytes = 0x00800000, + + /// + /// Align data on a 256-byte boundary. Valid only for object files. + /// + Align256Bytes = 0x00900000, + + /// + /// Align data on a 512-byte boundary. Valid only for object files. + /// + Align512Bytes = 0x00A00000, + + /// + /// Align data on a 1024-byte boundary. Valid only for object files. + /// + Align1024Bytes = 0x00B00000, + + /// + /// Align data on a 2048-byte boundary. Valid only for object files. + /// + Align2048Bytes = 0x00C00000, + + /// + /// Align data on a 4096-byte boundary. Valid only for object files. + /// + Align4096Bytes = 0x00D00000, + + /// + /// Align data on an 8192-byte boundary. Valid only for object files. + /// + Align8192Bytes = 0x00E00000, + + /// + /// The section contains extended relocations. + /// + LinkExtendedRelocationOverflow = 0x01000000, + + /// + /// The section can be discarded as needed. + /// + MemoryDiscardable = 0x02000000, + + /// + /// The section cannot be cached. + /// + MemoryNotCached = 0x04000000, + + /// + /// The section is not pageable. + /// + MemoryNotPaged = 0x08000000, + + /// + /// The section can be shared in memory. + /// + MemoryShared = 0x10000000, + + /// + /// The section can be executed as code. + /// + MemoryExecute = 0x20000000, + + /// + /// The section can be read. + /// + MemoryRead = 0x40000000, + + /// + /// The section can be written to. + /// + MemoryWrite = 0x80000000 + } + + + public class ImageOptionalHeaderWrapper { + public IMAGE_OPTIONAL_HEADER32 h32; + public IMAGE_OPTIONAL_HEADER64 h64; + public bool IsPe; + + + public uint CertificateTableVirtualAddress { + get { + if (IsPe) { + return h32.CertificateTable.VirtualAddress; + } else { + return h64.CertificateTable.VirtualAddress; + } + } + } + + public uint CertificateTableSize { + get { + if (IsPe) { + return h32.CertificateTable.Size; + } else { + return h64.CertificateTable.Size; + } + } + } + + public uint SizeOfHeaders { + get { + if (IsPe) { + return h32.SizeOfHeaders; + } else { + return h64.SizeOfHeaders; + } + } + } + } +} \ No newline at end of file diff --git a/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs new file mode 100644 index 00000000..8691f8f6 --- /dev/null +++ b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs @@ -0,0 +1,344 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.Pkcs; + +namespace ZitiUpdateService.Checkers.PeFile { + public class SignedFileValidator { + public const int LengthOfChecksum = 4; //the checksum is 4 bytes + public const int LenghtCertificateTable = 8; //the Certificate Table is 8 bytes + + public HashPositions ImportantHashPositions { get; private set; } + public string FilePath { get; private set; } + public PeType Type { get; private set; } + public IMAGE_FILE_HEADER CoffHeader { get; private set; } + public IMAGE_OPTIONAL_HEADER32 StandardFieldsPe32 { get; private set; } + public IMAGE_OPTIONAL_HEADER64 StandardFieldsPe32Plus { get; private set; } + public SignedFileValidator(string pathToFile) { + FilePath = pathToFile; + ImportantHashPositions = new HashPositions(); + + parse(); + if (ImportantHashPositions.reorderNeeded) { + ImportantHashPositions.SectionTableHeaders.Sort((x, y) => x.PointerToRawData.CompareTo(y.PointerToRawData)); + } + } + + // ReSharper disable once InconsistentNaming + private void parse() { + using (FileStream stream = new FileStream(FilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read)) + using (BinaryReader reader = new BinaryReader(stream)) { + + //seek to 0x3c to get right to the PE header location... + stream.Seek(0x3c, SeekOrigin.Begin); + uint startCoffHeader = BitConverter.ToUInt32(reader.ReadBytes(4), 0); + ImportantHashPositions.SetStartCoffHeader(startCoffHeader); + stream.Seek(startCoffHeader, SeekOrigin.Begin); + + //parse COFF Header (28 or 24 bytes): https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image + byte[] coffHeaderBytes = reader.ReadBytes(24); + CoffHeader = StructHelper.FromBytes(coffHeaderBytes); + + byte[] magicNumberBytes = reader.ReadBytes(2); + int magicNumber = BitConverter.ToInt16(magicNumberBytes, 0); + Type = magicNumber == 0x10b ? PeType.Pe32 : PeType.Pe32Plus; + stream.Seek(-2, SeekOrigin.Current); //skip back to before the magic number so the struct below is created properly + + //parse Optional Header Standard and Windows-Specific fields (66 or 88 bytes) + //see: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-standard-fields-image-only + if (Type == PeType.Pe32) { + byte[] headerBytes = reader.ReadBytes(Marshal.SizeOf(typeof(IMAGE_OPTIONAL_HEADER32))); + StandardFieldsPe32 = StructHelper.FromBytes(headerBytes); + } else { + byte[] headerBytes = reader.ReadBytes(Marshal.SizeOf(typeof(IMAGE_OPTIONAL_HEADER64))); + StandardFieldsPe32Plus = StructHelper.FromBytes(headerBytes); + } + + ImageOptionalHeaderWrapper wrapper = new ImageOptionalHeaderWrapper() { + h32 = StandardFieldsPe32, + h64 = StandardFieldsPe32Plus, + IsPe = Type == PeType.Pe32 + }; + ImportantHashPositions.SetCertificateDetails(Type, wrapper.SizeOfHeaders, wrapper.CertificateTableVirtualAddress, wrapper.CertificateTableSize); + + for (int i = 0; i < CoffHeader.NumberOfSections; i++) { + byte[] nextSection = reader.ReadBytes(Marshal.SizeOf(typeof(IMAGE_SECTION_HEADER))); //should be 40 bytes... + IMAGE_SECTION_HEADER sectionTableHeader = StructHelper.FromBytes(nextSection); + ImportantHashPositions.AddSectionTableHeader(sectionTableHeader); + } + } + } + + public void Verify() { + using (FileStream stream = new FileStream(FilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read)) + using (BinaryReader reader = new BinaryReader(stream)) { + //verify all the embedded signatures + stream.Seek(this.ImportantHashPositions.CertificateTableStart, SeekOrigin.Begin); + int readCertLen = reader.ReadInt32(); + int readRevision = reader.ReadInt16(); + int readCertType = reader.ReadInt16(); + byte[] pkcs7 = reader.ReadBytes(readCertLen); + + SignedCms cms = new SignedCms(); + cms.Decode(pkcs7); + cms.CheckHash(); + //shout out to Scott MG: https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/655f5c27-b049-4275-a8b3-cc1c0be2b4f2/retrieve-certificate-info-for-dualsigned-sha1sha256 + //for indicating that the unsigned attributes contain the other fingerprints + if (cms.SignerInfos.Count > 0) { + SignerInfo si = cms.SignerInfos[0]; + si.CheckSignature(true); + if (cms.SignerInfos[0] != null && cms.SignerInfos[0].UnsignedAttributes.Count > 0) { + foreach (AsnEncodedData asn in cms.SignerInfos[0].UnsignedAttributes[0].Values) { + SignedCms innerCms = new SignedCms(); + innerCms.Decode(asn.RawData); + innerCms.CheckHash(); + if (innerCms.SignerInfos.Count > 0) { + SignerInfo innerSignerInfo = innerCms.SignerInfos[0]; + innerSignerInfo.CheckSignature(false); + } + } + } + + //calculate the hash using the first signed info + var hashAlg = IncrementalHash.CreateHash(new HashAlgorithmName(si.DigestAlgorithm.FriendlyName.ToUpper())); + byte[] calculatedHash = CalculatePeHashStreaming(hashAlg); + string hash = BitConverter.ToString(calculatedHash).Replace("-", ""); + + string content = BitConverter.ToString(cms.ContentInfo.Content).Replace("-", ""); + if (!content.Contains(hash)) { + throw new CryptographicException("The expected hash and the actual hash did not match!"); + } + } + } + } + + public byte[] CalculatePeHashStreaming(IncrementalHash alg) { + using (FileStream stream = new FileStream(FilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read)) + using (BinaryReader reader = new BinaryReader(stream)) { + stream.Seek(ImportantHashPositions.Chunk1.Start, SeekOrigin.Begin); + alg.AppendData(reader.ReadBytes(ImportantHashPositions.Chunk1.Length)); + + stream.Seek(ImportantHashPositions.Chunk2.Start, SeekOrigin.Begin); + alg.AppendData(reader.ReadBytes(ImportantHashPositions.Chunk2.Length)); + + stream.Seek(ImportantHashPositions.Chunk3.Start, SeekOrigin.Begin); + alg.AppendData(reader.ReadBytes(ImportantHashPositions.Chunk3.Length)); + + int blockSize = 8192; + int read = 0; + byte[] buf = new byte[blockSize]; + + foreach (IMAGE_SECTION_HEADER h in ImportantHashPositions.SectionTableHeaders) { + stream.Seek(h.PointerToRawData, SeekOrigin.Begin); + if (h.SizeOfRawData <= blockSize) { + //just read the full block as one + alg.AppendData(reader.ReadBytes((int)h.SizeOfRawData)); + } else { + long pos = stream.Position; + long end = h.PointerToRawData + h.SizeOfRawData - blockSize; + //process the data into blocks + while (pos < end) { + read = stream.Read(buf, 0, blockSize); + alg.AppendData(buf, 0, read); + pos += blockSize; + } + + int remaining = (int)(end - pos + blockSize); + if (remaining > 0) { + read = stream.Read(buf, 0, remaining); + alg.AppendData(buf, 0, read); + } + } + } + + /* + 14. Create a value called FILE_SIZE, which is not part of the signature. Set this value to the + image’s file size, acquired from the underlying file system. If FILE_SIZE is greater than + SUM_OF_BYTES_HASHED, the file contains extra data that must be added to the hash. This data + begins at the SUM_OF_BYTES_HASHED file offset, and its length is: + (File Size) – ((Size of AttributeCertificateTable) + SUM_OF_BYTES_HASHED) + Note: The size of Attribute Certificate Table is specified in the second ULONG value in the + Certificate Table entry (32 bit: offset 132, 64 bit: offset 148) in Optional Header Data + Directories. + */ + long FILE_SIZE = new FileInfo(FilePath).Length; + + long leftoverBytesToHash = FILE_SIZE - ImportantHashPositions.SumOfBytesHashed - ImportantHashPositions.CertificateTableSize; + + if (leftoverBytesToHash > 0) { + //HashBlock(alg, reader, SUM_OF_BYTES_HASHED + certTableSize, FILE_SIZE); + stream.Seek(ImportantHashPositions.SumOfBytesHashed, SeekOrigin.Begin); + byte[] leftoverBytes = reader.ReadBytes((int)leftoverBytesToHash); + alg.AppendData(leftoverBytes); + } else { + //no more bytes to hash... + } + //alg.TransformFinalBlock(new byte[0], 0, 0); + + byte[] result = alg.GetHashAndReset(); + + return result; + } + } + + public class HashPositions { + internal HashPositions() { + Chunk1 = new Range(); + Chunk2 = new Range(); + Chunk3 = new Range(); + SectionTableHeaders = new List(); + } + /* + * These functions are all based from the document linked to from the bottom of: + * https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-standard-fields-image-only + * + * Link to the document: + * https://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/Authenticode_PE.docx + * + * Relevant section copied from that document: + * To calculate the hash value: + 1. Load the image header into memory. + 2. Initialize a hash algorithm context. + 3. Hash the image header from its base to immediately before the start of the + checksum address, as specified in Optional Header Windows-Specific Fields. + 4. Skip over the checksum, which is a 4-byte field. + 5. Hash everything from the end of the checksum field to immediately before the start of + the Certificate Table entry, as specified in Optional Header Data Directories. + 6. Get the Attribute Certificate Table address and size from the Certificate Table entry. + For details, see section 5.7 of the PE/COFF specification. + 7. Exclude the Certificate Table entry from the calculation and hash everything from the + end of the Certificate Table entry to the end of image header, including Section Table + (headers).The Certificate Table entry is 8 bytes long, as specified in Optional Header + Data Directories. + 8. Create a counter called SUM_OF_BYTES_HASHED, which is not part of the signature. Set + this counter to the SizeOfHeaders field, as specified in Optional Header Windows-Specific + Field. + 9. Build a temporary table of pointers to all of the section headers in the image. The + NumberOfSections field of COFF File Header indicates how big the table should be. Do not + include any section headers in the table whose SizeOfRawData field is zero. + 10. Using the PointerToRawData field (offset 20) in the referenced SectionHeader structure as + a key, arrange the table's elements in ascending order. In other words, sort the section + headers in ascending order according to the disk-file offset of the sections. + 11. Walk through the sorted table, load the corresponding section into memory, and hash the entire + section. Use the SizeOfRawData field in the SectionHeader structure to determine the amount + of data to hash. + 12. Add the section’s SizeOfRawData value to SUM_OF_BYTES_HASHED. + 13. Repeat steps 11 and 12 for all of the sections in the sorted table. + 14. Create a value called FILE_SIZE, which is not part of the signature. Set this value to the + image’s file size, acquired from the underlying file system. If FILE_SIZE is greater than + SUM_OF_BYTES_HASHED, the file contains extra data that must be added to the hash. This data + begins at the SUM_OF_BYTES_HASHED file offset, and its length is: + (File Size) – ((Size of AttributeCertificateTable) + SUM_OF_BYTES_HASHED) + Note: The size of Attribute Certificate Table is specified in the second ULONG value in the + Certificate Table entry (32 bit: offset 132, 64 bit: offset 148) in Optional Header Data + Directories. + 15. Finalize the hash algorithm context. + Note: This procedure uses offset values from the PE/COFF specification, version 8.1 . + For authoritative offset values, refer to the most recent version of the PE/COFF specification. + */ + + public List SectionTableHeaders { get; private set; } + + /// + /// Chunk1 Start will set the position in the file which the hashing of the file should begin from + /// This is always the start of the file and thus is hardcoded to 0 and takes no parameters + /// + /// Chunk1 End is defined to be: "immediately before the start of the checksum address" + /// + /// Relevant step: + /// 3. Hash the image header from its base to immediately before the start of the + /// checksum address, as specified in Optional Header Windows-Specific Fields. + /// + public Range Chunk1 { get; private set; } + + /// + /// Chunk2 Start is defined to be: "end of the checksum field" + /// Chunk2 End is defined to be: "immediately before the start of the Certificate Table entry" + /// + /// Relevant step: + /// 5. Hash everything from the end of the checksum field to immediately before the start of + /// the Certificate Table entry, as specified in Optional Header Data Directories. + /// + public Range Chunk2 { get; private set; } + + /// + /// Chunk3 Start is defined to be: "the end of the Certificate Table entry" + /// Chunk3 End is defined to be: "the end of image header" + /// When testing/developing this solution - this was found to be the position specified by + /// the SizeOfHeaders field + /// + /// Relevant step: + /// 7. Exclude the Certificate Table entry from the calculation and hash everything from the + /// end of the Certificate Table entry to the end of image header, including Section Table + /// (headers).The Certificate Table entry is 8 bytes long, as specified in Optional Header + /// Data Directories. + /// + public Range Chunk3 { get; private set; } + + public uint SizeOfHeaders { get; private set; } + public uint SumOfBytesHashed { get; private set; } + public long CertificateTableStart { get; private set; } + public long CertificateTableSize { get; private set; } + + internal bool reorderNeeded = false; + private uint lastAddress = 0; + public void AddSectionTableHeader(IMAGE_SECTION_HEADER sectionTableHeader) { + + if (sectionTableHeader.SizeOfRawData > 0) { + SectionTableHeaders.Add(sectionTableHeader); + } + + if (lastAddress < sectionTableHeader.PointerToRawData) { + //good - it's in order... + } else { + reorderNeeded = true; + } + lastAddress = sectionTableHeader.PointerToRawData; + SumOfBytesHashed += sectionTableHeader.SizeOfRawData; + } + + public void SetStartCoffHeader(uint peHeaderStart) { + Chunk1.Start = 0; + uint checksumStart = peHeaderStart + 88; //88 is the size of the COFF Header (24) + Standard Headers + Windows-Specific Fields up to checksum (offset 64) + Chunk1.End = checksumStart; + Chunk2.Start = checksumStart + LengthOfChecksum; + + //and the four tables before the Certificate Table + //(ExportTable, ImportTable, ResourceTable, ExceptionTable) + uint certHeaderTableOffset = checksumStart + 64; + + Chunk2.End = certHeaderTableOffset; + Chunk3.Start = certHeaderTableOffset + LenghtCertificateTable; + } + + public void SetCertificateDetails(PeType type, uint sizeOfHeaders, uint certificateTableStart, + uint certificateTableSize) { + if (type == PeType.Pe32Plus) { + uint winSpecificExtraBytes = 16; //need to account for the extra bytes in the Windows-Specific Fields (each field has 4 extra bytes) + Chunk2.End += winSpecificExtraBytes; + Chunk3.Start += winSpecificExtraBytes; + } + + Chunk3.End = sizeOfHeaders; + SizeOfHeaders = sizeOfHeaders; + SumOfBytesHashed = sizeOfHeaders; + CertificateTableStart = certificateTableStart; + CertificateTableSize = certificateTableSize; + } + } + + public class Range { + public long Start { get; internal set; } + public long End { get; internal set; } + + public int Length => (int)(End - Start); + } + } + + public enum PeType { + Pe32, + Pe32Plus + } +} diff --git a/ZitiUpdateService/checkers/PeFile/StructHelper.cs b/ZitiUpdateService/checkers/PeFile/StructHelper.cs new file mode 100644 index 00000000..8e655822 --- /dev/null +++ b/ZitiUpdateService/checkers/PeFile/StructHelper.cs @@ -0,0 +1,17 @@ +using System.Runtime.InteropServices; + +namespace ZitiUpdateService.Checkers.PeFile { + public static class StructHelper { + + public static T FromBytes(byte[] bytes) { + //byte[] bytes = reader.ReadBytes(Marshal.SizeOf(typeof(T))); + + // Pin the managed memory while, copy it out the data, then unpin it + GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); + T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); + handle.Free(); + + return theStructure; + } + } +} diff --git a/ZitiUpdateService/checkers/UpdateCheckers.cs b/ZitiUpdateService/checkers/UpdateCheckers.cs index 582a15d8..374f1113 100644 --- a/ZitiUpdateService/checkers/UpdateCheckers.cs +++ b/ZitiUpdateService/checkers/UpdateCheckers.cs @@ -3,7 +3,7 @@ using System.IO; using NLog; -namespace ZitiUpdateService.checkers { +namespace ZitiUpdateService.Checkers { abstract class UpdateCheck { public abstract int IsUpdateAvailable(Version current); diff --git a/ZitiUpdateService/packages.config b/ZitiUpdateService/packages.config index 2acb7a14..28608793 100644 --- a/ZitiUpdateService/packages.config +++ b/ZitiUpdateService/packages.config @@ -5,4 +5,5 @@ - + + \ No newline at end of file From 778ca8d54cb903439024cf21420b15c8bbb7edc2 Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Fri, 28 May 2021 18:18:20 -0400 Subject: [PATCH 02/19] add openziti.rsa.root.signing.ca.pem as a resource --- ZitiUpdateService/Properties/AssemblyInfo.cs | 4 ++-- ZitiUpdateService/ZitiUpdateService.csproj | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ZitiUpdateService/Properties/AssemblyInfo.cs b/ZitiUpdateService/Properties/AssemblyInfo.cs index 03a5efb4..c384a42b 100644 --- a/ZitiUpdateService/Properties/AssemblyInfo.cs +++ b/ZitiUpdateService/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.9.6.0")] -[assembly: AssemblyVersion("1.9.6.0")] -[assembly: AssemblyFileVersion("1.9.6.0")] +[assembly: AssemblyVersion("1.9.4.0")] +[assembly: AssemblyFileVersion("1.9.4.0")] diff --git a/ZitiUpdateService/ZitiUpdateService.csproj b/ZitiUpdateService/ZitiUpdateService.csproj index 6f14703f..59e18a2a 100644 --- a/ZitiUpdateService/ZitiUpdateService.csproj +++ b/ZitiUpdateService/ZitiUpdateService.csproj @@ -13,6 +13,7 @@ true true false + publish\ true Disk @@ -28,7 +29,6 @@ false true true - x64 @@ -143,6 +143,7 @@ Always + Always From a62f3701f71292213951524c4564a7eb45fd5372 Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Thu, 3 Jun 2021 10:06:25 -0400 Subject: [PATCH 03/19] checkpoint commit --- ZitiUpdateService/App.config | 20 +- ZitiUpdateService/UpdateService.cs | 17 +- ZitiUpdateService/ZitiUpdateService.csproj | 16 +- .../checkers/PeFile/SignedFileValidator.cs | 316 +++++++++++++++++- .../checkers/PeFile/StructHelper.cs | 2 - .../checkers/PeFile/Win32Crypto.cs | 249 ++++++++++++++ .../PeFile/openziti.rootCA.rsa.pem.txt | 33 ++ ZitiUpdateService/packages.config | 4 + service/cziti/dns-proxy.go | 3 + 9 files changed, 635 insertions(+), 25 deletions(-) create mode 100644 ZitiUpdateService/checkers/PeFile/Win32Crypto.cs create mode 100644 ZitiUpdateService/checkers/PeFile/openziti.rootCA.rsa.pem.txt diff --git a/ZitiUpdateService/App.config b/ZitiUpdateService/App.config index df91ae79..e2c58379 100644 --- a/ZitiUpdateService/App.config +++ b/ZitiUpdateService/App.config @@ -1,4 +1,4 @@ - + - + - - - + + + - + - + - + - - + + diff --git a/ZitiUpdateService/UpdateService.cs b/ZitiUpdateService/UpdateService.cs index 59f7b246..418d8527 100644 --- a/ZitiUpdateService/UpdateService.cs +++ b/ZitiUpdateService/UpdateService.cs @@ -609,7 +609,7 @@ namespace ZitiUpdateService { inUpdateCheck = true; //simple semaphore try { Logger.Debug("checking for update"); - + /* int avail = check.IsUpdateAvailable(assemblyVersion); if (avail >= 0) { Logger.Debug("update check complete. no update available"); @@ -642,6 +642,21 @@ namespace ZitiUpdateService { } Logger.Debug("downloaded file hash was correct. update can continue."); + */ + + + + + + string fileDestination = @"C:\temp\signingtest\a\zz.exe"; + + + + + + + + new SignedFileValidator(fileDestination).Verify(); // check digital signature diff --git a/ZitiUpdateService/ZitiUpdateService.csproj b/ZitiUpdateService/ZitiUpdateService.csproj index 59e18a2a..78d8eab1 100644 --- a/ZitiUpdateService/ZitiUpdateService.csproj +++ b/ZitiUpdateService/ZitiUpdateService.csproj @@ -98,6 +98,16 @@ ..\packages\Microsoft.PowerShell.5.ReferenceAssemblies.1.1.0\lib\net4\System.Management.Automation.dll + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll + @@ -105,6 +115,9 @@ + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + @@ -120,6 +133,7 @@ + Component @@ -143,7 +157,6 @@ Always - Always @@ -158,6 +171,7 @@ + diff --git a/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs index 8691f8f6..351d98d3 100644 --- a/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs +++ b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs @@ -1,14 +1,22 @@ using System; using System.Collections.Generic; using System.IO; +using System.Net.Http; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Security.Cryptography.Pkcs; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading.Tasks; +using NLog; +using VerifyingFiles.PInvoke; namespace ZitiUpdateService.Checkers.PeFile { public class SignedFileValidator { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + public const int LengthOfChecksum = 4; //the checksum is 4 bytes - public const int LenghtCertificateTable = 8; //the Certificate Table is 8 bytes + public const int LengthCertificateTable = 8; //the Certificate Table is 8 bytes public HashPositions ImportantHashPositions { get; private set; } public string FilePath { get; private set; } @@ -16,7 +24,11 @@ namespace ZitiUpdateService.Checkers.PeFile { public IMAGE_FILE_HEADER CoffHeader { get; private set; } public IMAGE_OPTIONAL_HEADER32 StandardFieldsPe32 { get; private set; } public IMAGE_OPTIONAL_HEADER64 StandardFieldsPe32Plus { get; private set; } + + private readonly X509Certificate2 expectedRootCa = null; + public SignedFileValidator(string pathToFile) { + expectedRootCa = certFromResource("ZitiUpdateService.checkers.PeFile.openziti.rootCA.rsa.pem.txt"); FilePath = pathToFile; ImportantHashPositions = new HashPositions(); @@ -26,6 +38,16 @@ namespace ZitiUpdateService.Checkers.PeFile { } } + private X509Certificate2 certFromResource(string resourceName) { + var assembly = this.GetType().Assembly; + using (Stream stream = assembly.GetManifestResourceStream(resourceName)) + using (BinaryReader reader = new BinaryReader(stream)) { + byte[] bytes = new byte[stream.Length]; + stream.Read(bytes, 0, bytes.Length); + return new X509Certificate2(bytes); + } + } + // ReSharper disable once InconsistentNaming private void parse() { using (FileStream stream = new FileStream(FilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read)) @@ -71,8 +93,76 @@ namespace ZitiUpdateService.Checkers.PeFile { } } + public List ExtractVerifiedSignatures() { + List list = new List(); + using (FileStream stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read)) + using (BinaryReader reader = new BinaryReader(stream)) { + //verify all the embedded signatures + stream.Seek(this.ImportantHashPositions.CertificateTableStart, SeekOrigin.Begin); + int readCertLen = reader.ReadInt32(); + int readRevision = reader.ReadInt16(); + int readCertType = reader.ReadInt16(); + byte[] pkcs7 = reader.ReadBytes(readCertLen); + + SignedCms cms = new SignedCms(); + cms.Decode(pkcs7); + cms.CheckHash(); + + //shout out to Scott MG: https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/655f5c27-b049-4275-a8b3-cc1c0be2b4f2/retrieve-certificate-info-for-dualsigned-sha1sha256 + //for indicating that the unsigned attributes contain the other SignerInfos + foreach (var cmsSi in cms.SignerInfos) { + cmsSi.CheckSignature(true); + if (VerifyTrust(expectedRootCa, cmsSi.Certificate).Result) { + VerifyFileHash(cms, cmsSi); + list.Add(cms); + } + if (cmsSi.UnsignedAttributes.Count > 0) { + foreach (var unsignedAttr in cmsSi.UnsignedAttributes) { + foreach (AsnEncodedData asn in unsignedAttr.Values) { + SignedCms innerCms = new SignedCms(); + innerCms.Decode(asn.RawData); + innerCms.CheckHash(); + if (innerCms.SignerInfos.Count > 0) { + SignerInfo innerSignerInfo = innerCms.SignerInfos[0]; + try { + innerSignerInfo.CheckSignature(false); + } + catch (CryptographicException ce) { + if (innerSignerInfo.Certificate.Thumbprint == "39636E9F5E80308DE370C914CE8112876ECF4E0C") { + //special handling for the known 'old' NetFoundry signing certificate, now expired... + //TODO: remove this code after 2021 + //just allow this one error + Logger.Warn("Ignoring timestamp validity issue for the existing code signing certificate. Subject: {0}, Thumbprint: {1}", innerSignerInfo.Certificate.Subject, innerSignerInfo.Certificate.Thumbprint); + } else { + throw; + } + } + + if (VerifyTrust(expectedRootCa, innerSignerInfo.Certificate).Result) { + VerifyFileHash(innerCms, innerSignerInfo); + list.Add(innerCms); + } + } + } + } + } + } + } + + return list; + } + public void Verify() { - using (FileStream stream = new FileStream(FilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read)) + List list = ExtractVerifiedSignatures(); + foreach (SignedCms cms in list) { + //ignore entries with a version of 3 since this indicates a timestamp signature and not one we + Console.WriteLine(cms.Certificates); + } + } + + /* + public void Verify() { + using (FileStream stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read)) using (BinaryReader reader = new BinaryReader(stream)) { //verify all the embedded signatures stream.Seek(this.ImportantHashPositions.CertificateTableStart, SeekOrigin.Begin); @@ -89,6 +179,10 @@ namespace ZitiUpdateService.Checkers.PeFile { if (cms.SignerInfos.Count > 0) { SignerInfo si = cms.SignerInfos[0]; si.CheckSignature(true); + if (VerifyTrust(expectedRootCa, si.Certificate).Result) { + VerifyFileHash(cms, si); + return; //success! + } if (cms.SignerInfos[0] != null && cms.SignerInfos[0].UnsignedAttributes.Count > 0) { foreach (AsnEncodedData asn in cms.SignerInfos[0].UnsignedAttributes[0].Values) { SignedCms innerCms = new SignedCms(); @@ -97,23 +191,108 @@ namespace ZitiUpdateService.Checkers.PeFile { if (innerCms.SignerInfos.Count > 0) { SignerInfo innerSignerInfo = innerCms.SignerInfos[0]; innerSignerInfo.CheckSignature(false); + if (VerifyTrust(expectedRootCa, innerSignerInfo.Certificate).Result) { + VerifyFileHash(innerCms, innerSignerInfo); + return; //success! + } } } } + } - //calculate the hash using the first signed info - var hashAlg = IncrementalHash.CreateHash(new HashAlgorithmName(si.DigestAlgorithm.FriendlyName.ToUpper())); - byte[] calculatedHash = CalculatePeHashStreaming(hashAlg); - string hash = BitConverter.ToString(calculatedHash).Replace("-", ""); + throw new CryptographicException("No signing certificates found which meet the expected criteria"); + } + } + */ + public void VerifyFileHash(SignedCms cms, SignerInfo signerInfo) { + if (!signerInfo.Certificate.Subject.ToLower().Contains("netfoundry")) { + //just eject - no need to check this... + Logger.Debug("Not verifying certificate as it is not a NetFoundry signing certificate: {0}", signerInfo.Certificate.Subject); + return; + } + //calculate the hash using the first signed info + var hashAlg = IncrementalHash.CreateHash(new HashAlgorithmName(signerInfo.DigestAlgorithm.FriendlyName.ToUpper())); + byte[] calculatedHash = CalculatePeHashStreaming(hashAlg); + string hash = BitConverter.ToString(calculatedHash).Replace("-", ""); - string content = BitConverter.ToString(cms.ContentInfo.Content).Replace("-", ""); - if (!content.Contains(hash)) { - throw new CryptographicException("The expected hash and the actual hash did not match!"); + string content = BitConverter.ToString(cms.ContentInfo.Content).Replace("-", ""); + if (!content.Contains(hash)) { + throw new CryptographicException("The expected hash and the actual hash did not match!"); + } + } + + public async static Task VerifyTrust(X509Certificate2 trustedRootCertificateAuthority, X509Certificate2 certificate) { + + X509Store store = new X509Store("LocalMachine", StoreLocation.LocalMachine); + store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); + + X509Chain chain = new X509Chain(); + chain.ChainPolicy.ExtraStore.Add(trustedRootCertificateAuthority); + chain.ChainPolicy.ExtraStore.AddRange(store.Certificates); + chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag; + chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + chain.Build(new X509Certificate2(certificate)); + List exceptions = new List(); + foreach (X509ChainStatus status in chain.ChainStatus) { + if (status.Status == X509ChainStatusFlags.NoError || status.Status == X509ChainStatusFlags.UntrustedRoot || status.Status == X509ChainStatusFlags.NotTimeValid) { + //X509ChainStatusFlags.UntrustedRoot simply means it was not found in the computer/user's trust store.... that's fine... + //X509ChainStatusFlags.NotTimeValid means the certificate has expired - we're allowing this for now (summer 2021) to allow older clients to update + } else { + exceptions.Add(new CryptographicException("Could not verify trust: " + status.StatusInformation)); + } + } + + //final check - make sure the last certificate matches the expected thumbprint + if (chain.ChainElements[chain.ChainElements.Count - 1].Certificate.Thumbprint != trustedRootCertificateAuthority.Thumbprint) { + exceptions.Add(new Exception("Could not verify trust. The expected thumbprint was not found!")); + } + + bool crlFailure = false; + foreach (var e in certificate.Extensions) { + if (e.Oid.Value == "2.5.29.31") { //2.5.29.31 == CRL Distribution Points + try { + CrlDistributionPointParser t = new CrlDistributionPointParser(e.RawData); + + if (t != null) { + foreach (string url in t.urls) { + try { + //good - things are going the way we want... keep going + //fetch the CRL from the url provided and parse it... + byte[] crlBytes = await new HttpClient().GetByteArrayAsync(url); + + if (crlBytes != null && crlBytes.Length > 0) { + //fetch the crl + Win32Crypto.CrlInfo info = Win32Crypto.FromBlob(crlBytes); + if (info.RevokedSerialNumbers.Contains(certificate.SerialNumber)) { + exceptions.Add(new CryptographicException("Serial number " + certificate.SerialNumber + " has been revoked.")); + crlFailure = true; + } + } else { + exceptions.Add(new CryptographicException("Could not retrieve revocation list from " + url + "- cannot verify trust")); + crlFailure = true; + } + } + catch (Exception innerException) { + exceptions.Add(new CryptographicException("crl at " + url + " could not be used", innerException)); + crlFailure = true; + } + } + } + } + catch (Exception ex) { + exceptions.Add(ex); + break; } } } + + if (crlFailure) { + throw new AggregateException(exceptions); + } + + return true; } - + public byte[] CalculatePeHashStreaming(IncrementalHash alg) { using (FileStream stream = new FileStream(FilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read)) using (BinaryReader reader = new BinaryReader(stream)) { @@ -310,7 +489,7 @@ namespace ZitiUpdateService.Checkers.PeFile { uint certHeaderTableOffset = checksumStart + 64; Chunk2.End = certHeaderTableOffset; - Chunk3.Start = certHeaderTableOffset + LenghtCertificateTable; + Chunk3.Start = certHeaderTableOffset + LengthCertificateTable; } public void SetCertificateDetails(PeType type, uint sizeOfHeaders, uint certificateTableStart, @@ -341,4 +520,119 @@ namespace ZitiUpdateService.Checkers.PeFile { Pe32, Pe32Plus } + + public class CRLDistributionPointsParser { + public static List GetCrlUrls(BinaryReader asn1Data) { + //read the first byte - make sure it's marker 0x30 for SEQUENCE + byte first = asn1Data.ReadByte(); + if (first != 0x30) { + throw new CryptographicException("Could not parse CRL Distribution Points. First byte not asn marker"); + } + + if (first > 127) { + //means there's more bytes to read that control the overall length of the next section + } + List rtn = new List(); + + return rtn; + } + + private static void readType(MemoryStream asn1Data) { + + } + + private static void readLen(byte lenByte, MemoryStream asn1Data) { + + } + /* + internal static CrlDistributionPointParser ParseTags(BinaryReader asn1Data, int asn1DataLen) { + + CrlDistributionPointParser tag = new CrlDistributionPointParser(asn1Data, asn1DataLen); + + return tag; + }*/ + } + + internal class CrlDistributionPointParser { + internal int offset; + internal int length; + internal List tags = new List(); + private CrlDistributionPointParser parent = null; + private AsnTagType type = AsnTagType.NONE; + internal List urls = new List(); + private int consumedBytes = 0; + + internal CrlDistributionPointParser(byte[] asn1Data) : this(new MemoryStream(asn1Data)) { + + } + + internal CrlDistributionPointParser(MemoryStream asn1Data) : this(null, asn1Data/*new BinaryReader(asn1Data)*/, 0) { + + } + + internal CrlDistributionPointParser(CrlDistributionPointParser parent, MemoryStream memoryStream, /*BinaryReader asn1Reader,*/ int offset) { + BinaryReader asn1Reader = new BinaryReader(memoryStream); + this.offset = offset; + type = readTag(asn1Reader); + length = readTagLen(asn1Reader); + this.parent = parent; + int headerBytes = consumedBytes; + while (this.consumedBytes < length + headerBytes) { + switch (type) { + case AsnTagType.SEQUENCE: + case AsnTagType.ARRAY_ELEMENT: + var t = new CrlDistributionPointParser(this, memoryStream, (int) memoryStream.Position); + consumedBytes += t.consumedBytes; + tags.Add(t); + this.urls.AddRange(t.urls); + break; + case AsnTagType.STRING: + consumedBytes += length; + urls.Add(System.Text.Encoding.UTF8.GetString(asn1Reader.ReadBytes(length))); + break; + } + } + } + + internal AsnTagType readTag(BinaryReader asn1Data) { + byte tagByte = asn1Data.ReadByte(); + if (tagByte != 0x30 && tagByte != 0xA0 && tagByte != 0x86) { + throw new CryptographicException("Could not parse CRL Distribution Points. First byte not asn SEQUENCE|ARRAY|STRING marker"); + } + + consumedBytes++; + if (tagByte == 0x30) return AsnTagType.SEQUENCE; + if (tagByte == 0xA0) return AsnTagType.ARRAY_ELEMENT; + if (tagByte == 0x86) return AsnTagType.STRING; + return AsnTagType.NONE; + } + + internal int readTagLen(BinaryReader asn1Data) { + int tagLen = asn1Data.ReadByte(); + consumedBytes++; + if (tagLen > 128) { + //means there's more bytes to read that control the overall length of the next section + int moreBytes = tagLen - 128; + byte[] lenBytes = new byte[4]; + byte[] d = asn1Data.ReadBytes(moreBytes); + + int pos = 0; + for (int i = d.Length - 1; i >= 0; i--) { + lenBytes[pos++] = d[i]; + } + + tagLen = BitConverter.ToInt32(lenBytes, 0); + consumedBytes += moreBytes; + } + + return tagLen; + } + } + + internal enum AsnTagType { + SEQUENCE = 0x30, + ARRAY_ELEMENT = 0xA0, + STRING = 0x86, + NONE = 0, + } } diff --git a/ZitiUpdateService/checkers/PeFile/StructHelper.cs b/ZitiUpdateService/checkers/PeFile/StructHelper.cs index 8e655822..60670764 100644 --- a/ZitiUpdateService/checkers/PeFile/StructHelper.cs +++ b/ZitiUpdateService/checkers/PeFile/StructHelper.cs @@ -4,8 +4,6 @@ namespace ZitiUpdateService.Checkers.PeFile { public static class StructHelper { public static T FromBytes(byte[] bytes) { - //byte[] bytes = reader.ReadBytes(Marshal.SizeOf(typeof(T))); - // Pin the managed memory while, copy it out the data, then unpin it GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); diff --git a/ZitiUpdateService/checkers/PeFile/Win32Crypto.cs b/ZitiUpdateService/checkers/PeFile/Win32Crypto.cs new file mode 100644 index 00000000..a515baf6 --- /dev/null +++ b/ZitiUpdateService/checkers/PeFile/Win32Crypto.cs @@ -0,0 +1,249 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.Text; + +namespace VerifyingFiles.PInvoke { + public static class Win32Crypto { + //based on https://docs.microsoft.com/en-us/archive/blogs/alejacma/how-to-get-information-from-a-crl-net + + #region APIs + [DllImport("CRYPT32.DLL", EntryPoint = "CryptQueryObject", CharSet = CharSet.Auto, SetLastError = true)] + public static extern Boolean CryptQueryObject( + Int32 dwObjectType, + [MarshalAs(UnmanagedType.LPWStr)] String pvObject, + Int32 dwExpectedContentTypeFlags, + Int32 dwExpectedFormatTypeFlags, + Int32 dwFlags, + IntPtr pdwMsgAndCertEncodingType, + IntPtr pdwContentType, + IntPtr pdwFormatType, + IntPtr phCertStore, + IntPtr phMsg, + ref IntPtr ppvContext + ); + [DllImport("CRYPT32.DLL", EntryPoint = "CryptQueryObject", CharSet = CharSet.Auto, SetLastError = true)] + public static extern Boolean CryptQueryObject2( + Int32 dwObjectType, + IntPtr pvObject, + Int32 dwExpectedContentTypeFlags, + Int32 dwExpectedFormatTypeFlags, + Int32 dwFlags, + IntPtr pdwMsgAndCertEncodingType, + IntPtr pdwContentType, + IntPtr pdwFormatType, + ref IntPtr phCertStore, + IntPtr phMsg, + ref IntPtr ppvContext + ); + + [DllImport("CRYPT32.DLL", EntryPoint = "CertFreeCRLContext", SetLastError = true)] + public static extern Boolean CertFreeCRLContext( + IntPtr pCrlContext + ); + + [DllImport("CRYPT32.DLL", EntryPoint = "CertNameToStr", CharSet = CharSet.Auto, SetLastError = true)] + public static extern Int32 CertNameToStr( + Int32 dwCertEncodingType, + ref CRYPTOAPI_BLOB pName, + Int32 dwStrType, + StringBuilder psz, + Int32 csz + ); + + [DllImport("CRYPT32.DLL", EntryPoint = "CertFindExtension", CharSet = CharSet.Auto, SetLastError = true)] + public static extern IntPtr CertFindExtension( + [MarshalAs(UnmanagedType.LPStr)] String pszObjId, + Int32 cExtensions, + IntPtr rgExtensions + ); + + [DllImport("CRYPT32.DLL", EntryPoint = "CryptFormatObject", CharSet = CharSet.Auto, SetLastError = true)] + public static extern Boolean CryptFormatObject( + Int32 dwCertEncodingType, + Int32 dwFormatType, + Int32 dwFormatStrType, + IntPtr pFormatStruct, + [MarshalAs(UnmanagedType.LPStr)] String lpszStructType, + IntPtr pbEncoded, + Int32 cbEncoded, + StringBuilder pbFormat, + ref Int32 pcbFormat + ); + + #endregion APIs + + #region Structs + + [StructLayout(LayoutKind.Sequential)] + public struct CRL_CONTEXT { + public Int32 dwCertEncodingType; + public IntPtr pbCrlEncoded; + public Int32 cbCrlEncoded; + public IntPtr pCrlInfo; + public IntPtr hCertStore; + } + + [StructLayout(LayoutKind.Sequential)] + public struct CRL_INFO { + public Int32 dwVersion; + public CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm; + public CRYPTOAPI_BLOB Issuer; + public FILETIME ThisUpdate; + public FILETIME NextUpdate; + public Int32 cCRLEntry; + public IntPtr rgCRLEntry; + public Int32 cExtension; + public IntPtr rgExtension; + } + + [StructLayout(LayoutKind.Sequential)] + public struct CRYPT_ALGORITHM_IDENTIFIER { + [MarshalAs(UnmanagedType.LPStr)] public String pszObjId; + public CRYPTOAPI_BLOB Parameters; + } + + [StructLayout(LayoutKind.Sequential)] + public struct CRYPTOAPI_BLOB { + public Int32 cbData; + public IntPtr pbData; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FILETIME { + public Int32 dwLowDateTime; + public Int32 dwHighDateTime; + } + + [StructLayout(LayoutKind.Sequential)] + public struct CRL_ENTRY { + public CRYPTOAPI_BLOB SerialNumber; + public FILETIME RevocationDate; + public Int32 cExtension; + public IntPtr rgExtension; + } + + [StructLayout(LayoutKind.Sequential)] + public struct CERT_EXTENSION { + [MarshalAs(UnmanagedType.LPStr)] public String pszObjId; + public Boolean fCritical; + public CRYPTOAPI_BLOB Value; + } + + #endregion Structs + + #region Consts + + public const Int32 CERT_QUERY_OBJECT_BLOB = 0x00000002; + public const Int32 CERT_QUERY_CONTENT_CRL = 3; + public const Int32 CERT_QUERY_CONTENT_FLAG_CRL = 1 << CERT_QUERY_CONTENT_CRL; + public const Int32 CERT_QUERY_FORMAT_BINARY = 1; + public const Int32 CERT_QUERY_FORMAT_BASE64_ENCODED = 2; + public const Int32 CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED = 3; + public const Int32 CERT_QUERY_FORMAT_FLAG_BINARY = 1 << CERT_QUERY_FORMAT_BINARY; + public const Int32 CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED = 1 << CERT_QUERY_FORMAT_BASE64_ENCODED; + public const Int32 CERT_QUERY_FORMAT_FLAG_ASN_ASCII_HEX_ENCODED = 1 << CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED; + public const Int32 CERT_QUERY_FORMAT_FLAG_ALL = CERT_QUERY_FORMAT_FLAG_BINARY | CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED | CERT_QUERY_FORMAT_FLAG_ASN_ASCII_HEX_ENCODED; + + public const Int32 X509_ASN_ENCODING = 0x00000001; + public const Int32 PKCS_7_ASN_ENCODING = 0x00010000; + + public const Int32 X509_NAME = 7; + + public const Int32 CERT_SIMPLE_NAME_STR = 1; + public const Int32 CERT_OID_NAME_STR = 2; + public const Int32 CERT_X500_NAME_STR = 3; + + public const String szOID_CRL_REASON_CODE = "2.5.29.21"; + + #endregion + + private static List GetRevokedGetSerialNumbers(CRL_INFO stCrlInfo) { + List rtn = new List(); + var rgCrlEntry = stCrlInfo.rgCRLEntry; + + for (var i = 0; i < stCrlInfo.cCRLEntry; i++) { + var serial = string.Empty; + var stCrlEntry = (CRL_ENTRY)Marshal.PtrToStructure(rgCrlEntry, typeof(CRL_ENTRY)); + + IntPtr pByte = stCrlEntry.SerialNumber.pbData; + for (var j = 0; j < stCrlEntry.SerialNumber.cbData; j++) { + Byte bByte = Marshal.ReadByte(pByte); + serial = bByte.ToString("X").PadLeft(2, '0') + serial; + pByte = pByte + Marshal.SizeOf(typeof(byte)); + + } + rtn.Add(serial); + rgCrlEntry = rgCrlEntry + Marshal.SizeOf(typeof(CRL_ENTRY)); + } + + return rtn; + } + + public static DateTime FileTimeToDateTime(FILETIME fileTime) { + DateTime dateTime; + IntPtr int64Ptr = Marshal.AllocHGlobal(sizeof(Int64)); + try { + Marshal.StructureToPtr(fileTime, int64Ptr, true); + dateTime = DateTime.FromFileTime(Marshal.ReadInt64(int64Ptr)); + } finally { + Marshal.FreeHGlobal(int64Ptr); + } + + return dateTime; + } + + public static CrlInfo FromBlob(byte[] CrlDERBytes) { + + var phCertStore = IntPtr.Zero; + var pvContext = IntPtr.Zero; + var hCrlData = new GCHandle(); + var hCryptBlob = new GCHandle(); + try { + hCrlData = GCHandle.Alloc(CrlDERBytes, GCHandleType.Pinned); + CRYPTOAPI_BLOB stCryptBlob; + stCryptBlob.cbData = CrlDERBytes.Length; + stCryptBlob.pbData = hCrlData.AddrOfPinnedObject(); + hCryptBlob = GCHandle.Alloc(stCryptBlob, GCHandleType.Pinned); + + if (!CryptQueryObject2( + CERT_QUERY_OBJECT_BLOB, + hCryptBlob.AddrOfPinnedObject(), + CERT_QUERY_CONTENT_FLAG_CRL, + CERT_QUERY_FORMAT_FLAG_BINARY, + 0, + IntPtr.Zero, + IntPtr.Zero, + IntPtr.Zero, + ref phCertStore, + IntPtr.Zero, + ref pvContext + )) { + throw new Win32Exception(Marshal.GetLastWin32Error(), "CRL is Corrupted."); + } + var stCrlContext = (CRL_CONTEXT)Marshal.PtrToStructure(pvContext, typeof(CRL_CONTEXT)); + var stCrlInfo = (CRL_INFO)Marshal.PtrToStructure(stCrlContext.pCrlInfo, typeof(CRL_INFO)); + + CrlInfo info = new CrlInfo(); + info.validTo = FileTimeToDateTime(stCrlInfo.NextUpdate); + info.validFrom = FileTimeToDateTime(stCrlInfo.ThisUpdate); + info.RevokedSerialNumbers = GetRevokedGetSerialNumbers(stCrlInfo); + + return info; + } finally { + if (hCrlData.IsAllocated) hCrlData.Free(); + if (hCryptBlob.IsAllocated) hCryptBlob.Free(); + if (!pvContext.Equals(IntPtr.Zero)) { + CertFreeCRLContext(pvContext); + } + } + } + + public struct CrlInfo { + public DateTime validTo; + public DateTime validFrom; + public List RevokedSerialNumbers; + } + } +} diff --git a/ZitiUpdateService/checkers/PeFile/openziti.rootCA.rsa.pem.txt b/ZitiUpdateService/checkers/PeFile/openziti.rootCA.rsa.pem.txt new file mode 100644 index 00000000..87464af1 --- /dev/null +++ b/ZitiUpdateService/checkers/PeFile/openziti.rootCA.rsa.pem.txt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFozCCA4ugAwIBAgIUdhfTn8Eu4pU39MIPpoerTLqbLRkwDQYJKoZIhvcNAQEN +BQAwYTEYMBYGA1UEAwwPUm9vdCBTaWduaW5nIENBMRkwFwYDVQQKDBBvcGVueml0 +aS5vcmcgSW5jMRAwDgYDVQQLDAdhZHYtZGV2MQswCQYDVQQGEwJVUzELMAkGA1UE +CAwCTkMwHhcNMjEwNjAyMTcyNDMzWhcNNDEwNjE3MTcyNDMzWjBhMRgwFgYDVQQD +DA9Sb290IFNpZ25pbmcgQ0ExGTAXBgNVBAoMEG9wZW56aXRpLm9yZyBJbmMxEDAO +BgNVBAsMB2Fkdi1kZXYxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOQzCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBALcLp/jh0zYqDQw6Gxzoxnib08QLwBo7 +AHLdJSOxdFy3K0a7xSEZjkhJgWQWw/c/HArw5PiHS4jdsh7o8mSJGLqBj+s/Gj4M +tM4k3A5jJHcgAbDKPAaAZAXQ2aEDp8eA65S1mRqNnYHbmTm3Q3t3U7dqI1lISzjz +k5jBQnXS3Qtlj2SyMXRYPpPJIQlXgZXdvLmL7wG1FpmCFBSEEdOVMIhEr5hJ/5rc +HpTRSueLPpdJUSv3B3uuPdo5rI/pJJ6gAJUtmMw/G8CAlMwXlUbSLt5IvNriyjqT +pisTjkQR0Vs8pvAN0CKfOqwoPWDwuJkump+LRTTQ0PLaNCnlU96mpoX9D7yhQAGP +C5h0N0jPV2MpnEFNVPaGE43Wj1zejVP1S5Ep3i/Xu/aSUGmIK5MQ95z/J927LBKe +AOyQDdQC0WkCBpCzpe0G9c+M0u7lYUHWXExWB22nQT2rM3aG5ZdOJRu42PXFNUr4 +bgkZq56PiCCgwDENOlln6/h8wD2a2o5n638BCwW3Qq4w8u2e7dKIAjNroQ053iyw +ZmSIwImf9N+0631d8LDoVLllry+geP1bSlI1UCYcVjJWGSDwEmK9h+kwUsMX+yl7 +TAzgrnAkBS+C62sDvGHRjhp82IDklqsBQV7SsRG5ZqHAipbIa0NfCURsqmQ6iyNR +a3/9oiE9ARdHAgMBAAGjUzBRMB0GA1UdDgQWBBS0v5OYBy16UEw7k7nMLg7Y3Fs7 +uDAfBgNVHSMEGDAWgBS0v5OYBy16UEw7k7nMLg7Y3Fs7uDAPBgNVHRMBAf8EBTAD +AQH/MA0GCSqGSIb3DQEBDQUAA4ICAQBPtyCaKyk+WhXI1Gu83h6ePKuevwwnBygr +xOEJM7b0kAxtcgK46B9wPgvLuUw1znmDzAuAqU52k00q1xLGZ+N5JNhlzzNWW0n2 +8Kj0gFLmu3tO7XXjQfQPKiZp/iiwOG9XPVVDTF2HVqkOsvdGYD2XH+wI4v/4m3wa +xTvliEz9NCei7/NOhuTbJvbvcSmqAvs3+gH55bzBvvr+Hx/krx8HMcBikx92S5nF +NKBkHIUQVqGoCHxGY/uM0xou5797faVEzLkaDle+oisMiy061pYI0bb9CRdHAU0r +UoemoY3UOKlyK57wdXKRizDxmEyND2zxPYrktZu0SybjZmaFOCHLLp1WrcTlcp3C +7Lsuj9Vzui2mjJuQzSTEyd1R04AT3xwb9slN+iHuN1dAGexVsENahW2q9ZAjEtbu +E6B+gOrSJ7WaL8R7AAz/E3tfU2bYm99JoGCPjI2/kP7udw7EqvoKYfJO1gbRo45z +37xJmDrxb2SJFEtn5mqPO63HjGXGiC6xI52Olmv4Csk3WOseO3j9/SwazDAMqZSh +dY0BN0DADO8kiohyF+Cq7nPR/tX17DpQiWFgJ4xoYHRFYCyb6QZ5jOzMhrZJa7cM +v7BIbBcUe1vcKJ+gD6dXMT77KakdkRSfuiuEAeh6GmjPC130ni1stn+chw0pymax +ZSpzkClEkQ== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/ZitiUpdateService/packages.config b/ZitiUpdateService/packages.config index 28608793..4e54d782 100644 --- a/ZitiUpdateService/packages.config +++ b/ZitiUpdateService/packages.config @@ -5,5 +5,9 @@ + + + + \ No newline at end of file diff --git a/service/cziti/dns-proxy.go b/service/cziti/dns-proxy.go index 94b1002a..1e311972 100644 --- a/service/cziti/dns-proxy.go +++ b/service/cziti/dns-proxy.go @@ -354,6 +354,7 @@ outer: for { select { case pr := <-proxiedRequests: + log.Warnf("i am in proxiedRequests") id := (uint32(pr.req.Id) << 16) | uint32(pr.req.Question[0].Qtype) reqs[id] = pr b, _ := pr.req.Pack() @@ -376,6 +377,7 @@ outer: } case rep := <-respChan: + log.Warnf("i am in respChan") reply := dns.Msg{} if err := reply.Unpack(rep); err == nil { id := (uint32(reply.Id) << 16) | uint32(reply.Question[0].Qtype) @@ -394,6 +396,7 @@ outer: } } case <-time.After(time.Minute): + log.Warnf("i am in time.After") // cleanup requests we didn't get answers for now := time.Now() for k, r := range reqs { From 63142a1d8f46293fddc3f315fb64aa9dcc751a7f Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Thu, 3 Jun 2021 14:15:51 -0400 Subject: [PATCH 04/19] next round of fixes - ready to push to beta and test --- ZitiUpdateService/UpdateService.cs | 34 +------ .../checkers/PeFile/SignedFileValidator.cs | 92 ++++++++----------- 2 files changed, 41 insertions(+), 85 deletions(-) diff --git a/ZitiUpdateService/UpdateService.cs b/ZitiUpdateService/UpdateService.cs index 418d8527..e423071e 100644 --- a/ZitiUpdateService/UpdateService.cs +++ b/ZitiUpdateService/UpdateService.cs @@ -609,7 +609,7 @@ namespace ZitiUpdateService { inUpdateCheck = true; //simple semaphore try { Logger.Debug("checking for update"); - /* + int avail = check.IsUpdateAvailable(assemblyVersion); if (avail >= 0) { Logger.Debug("update check complete. no update available"); @@ -642,40 +642,8 @@ namespace ZitiUpdateService { } Logger.Debug("downloaded file hash was correct. update can continue."); - */ - - - - - - string fileDestination = @"C:\temp\signingtest\a\zz.exe"; - - - - - - - - new SignedFileValidator(fileDestination).Verify(); - // check digital signature - var signer = X509Certificate.CreateFromSignedFile(fileDestination); - /* keep these commented out lines - just in case we need all the certs from the file use this - var coll = new X509Certificate2Collection(); - coll.Import(filePath); - */ - - var subject = signer.Subject; - if (!expected_subject.Contains(subject)) { - Logger.Error("the file downloaded uses a subject that is unknown! the installation will not proceed. [subject:{0}]", subject); - inUpdateCheck = false; - return; - - } else { - Logger.Info("the file downloaded uses a known subject. installation and can proceed. [subject:{0}]", subject); - } - StopZiti(); StopUI().Wait(); diff --git a/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs index 351d98d3..56ef02c8 100644 --- a/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs +++ b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs @@ -93,8 +93,18 @@ namespace ZitiUpdateService.Checkers.PeFile { } } - public List ExtractVerifiedSignatures() { - List list = new List(); + private void addCertToList(SignerInfo si, List list) { + + X509Certificate2 cert = si.Certificate; + if (list.Find(x => x.Thumbprint == cert.Thumbprint) == null) { + list.Add(cert); + } else { + Logger.Debug("Certificate with Thumbprint {0} already in list. skipping.", cert.Thumbprint); + } + } + + public List ExtractVerifiedSignatureCertificates() { + List list = new List(); using (FileStream stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read)) using (BinaryReader reader = new BinaryReader(stream)) { //verify all the embedded signatures @@ -114,7 +124,7 @@ namespace ZitiUpdateService.Checkers.PeFile { cmsSi.CheckSignature(true); if (VerifyTrust(expectedRootCa, cmsSi.Certificate).Result) { VerifyFileHash(cms, cmsSi); - list.Add(cms); + addCertToList(cmsSi, list); } if (cmsSi.UnsignedAttributes.Count > 0) { foreach (var unsignedAttr in cmsSi.UnsignedAttributes) { @@ -134,13 +144,17 @@ namespace ZitiUpdateService.Checkers.PeFile { //just allow this one error Logger.Warn("Ignoring timestamp validity issue for the existing code signing certificate. Subject: {0}, Thumbprint: {1}", innerSignerInfo.Certificate.Subject, innerSignerInfo.Certificate.Thumbprint); } else { - throw; + if (IsCertificateOpenZitiVerifies(innerSignerInfo.Certificate)) { + Logger.Debug("Signature could not be verified. This is expected for the certificates issued by openziti/netfoundry."); + } else { + throw; + } } } if (VerifyTrust(expectedRootCa, innerSignerInfo.Certificate).Result) { VerifyFileHash(innerCms, innerSignerInfo); - list.Add(innerCms); + addCertToList(innerSignerInfo, list); } } } @@ -152,64 +166,38 @@ namespace ZitiUpdateService.Checkers.PeFile { return list; } - public void Verify() { - List list = ExtractVerifiedSignatures(); - foreach (SignedCms cms in list) { - //ignore entries with a version of 3 since this indicates a timestamp signature and not one we - Console.WriteLine(cms.Certificates); + public bool IsCertificateOpenZitiVerifies(X509Certificate2 cert) { + bool yesOrNo = cert.Subject.ToLower().Contains("netfoundry") || cert.Subject.ToLower().Contains("openziti"); + if (yesOrNo) { + Logger.Debug("Certificate is one openziti verifies: {0}", cert.Subject); } + return yesOrNo; } - /* public void Verify() { - using (FileStream stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read)) - using (BinaryReader reader = new BinaryReader(stream)) { - //verify all the embedded signatures - stream.Seek(this.ImportantHashPositions.CertificateTableStart, SeekOrigin.Begin); - int readCertLen = reader.ReadInt32(); - int readRevision = reader.ReadInt16(); - int readCertType = reader.ReadInt16(); - byte[] pkcs7 = reader.ReadBytes(readCertLen); + List list = ExtractVerifiedSignatureCertificates(); + foreach (X509Certificate2 cert in list) { + if (IsCertificateOpenZitiVerifies(cert)) { + //verify this certificate was issued from the known CA + try { + VerifyTrust(expectedRootCa, cert); - SignedCms cms = new SignedCms(); - cms.Decode(pkcs7); - cms.CheckHash(); - //shout out to Scott MG: https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/655f5c27-b049-4275-a8b3-cc1c0be2b4f2/retrieve-certificate-info-for-dualsigned-sha1sha256 - //for indicating that the unsigned attributes contain the other fingerprints - if (cms.SignerInfos.Count > 0) { - SignerInfo si = cms.SignerInfos[0]; - si.CheckSignature(true); - if (VerifyTrust(expectedRootCa, si.Certificate).Result) { - VerifyFileHash(cms, si); - return; //success! - } - if (cms.SignerInfos[0] != null && cms.SignerInfos[0].UnsignedAttributes.Count > 0) { - foreach (AsnEncodedData asn in cms.SignerInfos[0].UnsignedAttributes[0].Values) { - SignedCms innerCms = new SignedCms(); - innerCms.Decode(asn.RawData); - innerCms.CheckHash(); - if (innerCms.SignerInfos.Count > 0) { - SignerInfo innerSignerInfo = innerCms.SignerInfos[0]; - innerSignerInfo.CheckSignature(false); - if (VerifyTrust(expectedRootCa, innerSignerInfo.Certificate).Result) { - VerifyFileHash(innerCms, innerSignerInfo); - return; //success! - } - } - } + return; //yes! + } catch { + //empty on purpose. keep checking... } + } else { + Logger.Debug("Certificate is not one ziti verifies for: {0}", cert.Subject); } - - throw new CryptographicException("No signing certificates found which meet the expected criteria"); } + throw new CryptographicException("Executable not signed by an appropriate certificate"); } - */ + public void VerifyFileHash(SignedCms cms, SignerInfo signerInfo) { - if (!signerInfo.Certificate.Subject.ToLower().Contains("netfoundry")) { - //just eject - no need to check this... - Logger.Debug("Not verifying certificate as it is not a NetFoundry signing certificate: {0}", signerInfo.Certificate.Subject); + if (!IsCertificateOpenZitiVerifies(signerInfo.Certificate)) { return; } + //calculate the hash using the first signed info var hashAlg = IncrementalHash.CreateHash(new HashAlgorithmName(signerInfo.DigestAlgorithm.FriendlyName.ToUpper())); byte[] calculatedHash = CalculatePeHashStreaming(hashAlg); @@ -223,7 +211,7 @@ namespace ZitiUpdateService.Checkers.PeFile { public async static Task VerifyTrust(X509Certificate2 trustedRootCertificateAuthority, X509Certificate2 certificate) { - X509Store store = new X509Store("LocalMachine", StoreLocation.LocalMachine); + X509Store store = new X509Store("MY", StoreLocation.CurrentUser); store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); X509Chain chain = new X509Chain(); From 9b00b1b56762f0ef6633f904757aad26f8732436 Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Thu, 3 Jun 2021 14:41:55 -0400 Subject: [PATCH 05/19] version bump --- Installer/ZitiDesktopEdge.aip | 4 ++-- version | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Installer/ZitiDesktopEdge.aip b/Installer/ZitiDesktopEdge.aip index 891569b9..044042c0 100644 --- a/Installer/ZitiDesktopEdge.aip +++ b/Installer/ZitiDesktopEdge.aip @@ -23,10 +23,10 @@ - + - + diff --git a/version b/version index 7bc1c404..fee0a278 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.9.6 +1.9.7 From b0b7e2c4539a36025611b65f86b93f49b6d5304b Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Thu, 3 Jun 2021 16:41:07 -0400 Subject: [PATCH 06/19] minor tweaks after testing --- Installer/ZitiDesktopEdge.aip | 4 +- .../checkers/PeFile/SignedFileValidator.cs | 40 ++++++++++--------- version | 2 +- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/Installer/ZitiDesktopEdge.aip b/Installer/ZitiDesktopEdge.aip index 044042c0..7bbedc5c 100644 --- a/Installer/ZitiDesktopEdge.aip +++ b/Installer/ZitiDesktopEdge.aip @@ -23,10 +23,10 @@ - + - + diff --git a/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs index 56ef02c8..65c1af82 100644 --- a/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs +++ b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs @@ -26,8 +26,15 @@ namespace ZitiUpdateService.Checkers.PeFile { public IMAGE_OPTIONAL_HEADER64 StandardFieldsPe32Plus { get; private set; } private readonly X509Certificate2 expectedRootCa = null; + private readonly X509Store empty = new X509Store(); + private X509Store SystemCAs = new X509Store(); public SignedFileValidator(string pathToFile) { + X509Store store = new X509Store("MY", StoreLocation.CurrentUser); + store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); + SystemCAs.Open(OpenFlags.ReadWrite); + SystemCAs.AddRange(store.Certificates); + expectedRootCa = certFromResource("ZitiUpdateService.checkers.PeFile.openziti.rootCA.rsa.pem.txt"); FilePath = pathToFile; ImportantHashPositions = new HashPositions(); @@ -122,7 +129,7 @@ namespace ZitiUpdateService.Checkers.PeFile { //for indicating that the unsigned attributes contain the other SignerInfos foreach (var cmsSi in cms.SignerInfos) { cmsSi.CheckSignature(true); - if (VerifyTrust(expectedRootCa, cmsSi.Certificate).Result) { + if (VerifyTrust(SystemCAs, expectedRootCa, cmsSi.Certificate).Result) { VerifyFileHash(cms, cmsSi); addCertToList(cmsSi, list); } @@ -152,7 +159,7 @@ namespace ZitiUpdateService.Checkers.PeFile { } } - if (VerifyTrust(expectedRootCa, innerSignerInfo.Certificate).Result) { + if (VerifyTrust(SystemCAs, expectedRootCa, innerSignerInfo.Certificate).Result) { VerifyFileHash(innerCms, innerSignerInfo); addCertToList(innerSignerInfo, list); } @@ -169,7 +176,9 @@ namespace ZitiUpdateService.Checkers.PeFile { public bool IsCertificateOpenZitiVerifies(X509Certificate2 cert) { bool yesOrNo = cert.Subject.ToLower().Contains("netfoundry") || cert.Subject.ToLower().Contains("openziti"); if (yesOrNo) { - Logger.Debug("Certificate is one openziti verifies: {0}", cert.Subject); + Logger.Debug("Certificate does need verification: {0}", cert.Subject); + } else { + Logger.Debug("Certificate does not need verification: {0}", cert.Subject); } return yesOrNo; } @@ -180,14 +189,12 @@ namespace ZitiUpdateService.Checkers.PeFile { if (IsCertificateOpenZitiVerifies(cert)) { //verify this certificate was issued from the known CA try { - VerifyTrust(expectedRootCa, cert); - + VerifyTrust(empty, expectedRootCa, cert, true).Wait(); + Logger.Info("Certificate {0}:{1} was verified signed by {2}:{3}", cert.Thumbprint, cert.Subject, expectedRootCa.Thumbprint, expectedRootCa.Subject); return; //yes! } catch { //empty on purpose. keep checking... } - } else { - Logger.Debug("Certificate is not one ziti verifies for: {0}", cert.Subject); } } throw new CryptographicException("Executable not signed by an appropriate certificate"); @@ -209,14 +216,14 @@ namespace ZitiUpdateService.Checkers.PeFile { } } - public async static Task VerifyTrust(X509Certificate2 trustedRootCertificateAuthority, X509Certificate2 certificate) { + public async static Task VerifyTrust(X509Store cas, X509Certificate2 trustedRootCertificateAuthority, X509Certificate2 certificate) { + return await VerifyTrust(cas, trustedRootCertificateAuthority, certificate, false); + } - X509Store store = new X509Store("MY", StoreLocation.CurrentUser); - store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); - + public async static Task VerifyTrust(X509Store cas, X509Certificate2 trustedRootCertificateAuthority, X509Certificate2 certificate, bool verifyTrustedRoot) { X509Chain chain = new X509Chain(); chain.ChainPolicy.ExtraStore.Add(trustedRootCertificateAuthority); - chain.ChainPolicy.ExtraStore.AddRange(store.Certificates); + chain.ChainPolicy.ExtraStore.AddRange(cas.Certificates); chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag; chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.Build(new X509Certificate2(certificate)); @@ -231,11 +238,10 @@ namespace ZitiUpdateService.Checkers.PeFile { } //final check - make sure the last certificate matches the expected thumbprint - if (chain.ChainElements[chain.ChainElements.Count - 1].Certificate.Thumbprint != trustedRootCertificateAuthority.Thumbprint) { + if (verifyTrustedRoot && chain.ChainElements[chain.ChainElements.Count - 1].Certificate.Thumbprint != trustedRootCertificateAuthority.Thumbprint) { exceptions.Add(new Exception("Could not verify trust. The expected thumbprint was not found!")); } - bool crlFailure = false; foreach (var e in certificate.Extensions) { if (e.Oid.Value == "2.5.29.31") { //2.5.29.31 == CRL Distribution Points try { @@ -253,28 +259,24 @@ namespace ZitiUpdateService.Checkers.PeFile { Win32Crypto.CrlInfo info = Win32Crypto.FromBlob(crlBytes); if (info.RevokedSerialNumbers.Contains(certificate.SerialNumber)) { exceptions.Add(new CryptographicException("Serial number " + certificate.SerialNumber + " has been revoked.")); - crlFailure = true; } } else { exceptions.Add(new CryptographicException("Could not retrieve revocation list from " + url + "- cannot verify trust")); - crlFailure = true; } } catch (Exception innerException) { exceptions.Add(new CryptographicException("crl at " + url + " could not be used", innerException)); - crlFailure = true; } } } } catch (Exception ex) { exceptions.Add(ex); - break; } } } - if (crlFailure) { + if (exceptions.Count > 0) { throw new AggregateException(exceptions); } diff --git a/version b/version index fee0a278..66beabb5 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.9.7 +1.9.8 From 1fedec26335162516b5a27b7245c7052f8662f32 Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Thu, 3 Jun 2021 16:59:22 -0400 Subject: [PATCH 07/19] only allow for the one known thumbprint to report expired not any cert --- ZitiUpdateService/UpdateService.cs | 3 --- .../checkers/PeFile/SignedFileValidator.cs | 15 ++++++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/ZitiUpdateService/UpdateService.cs b/ZitiUpdateService/UpdateService.cs index e423071e..3ea20548 100644 --- a/ZitiUpdateService/UpdateService.cs +++ b/ZitiUpdateService/UpdateService.cs @@ -34,12 +34,9 @@ namespace ZitiUpdateService { public bool useGithubCheck { get; private set; } - private static string[] expected_subject = new string[] { @"CN=""NetFoundry, Inc."", O=""NetFoundry, Inc."", L=Herndon, S=Virginia, C=US", @"CN=NetFoundry Inc., O=NetFoundry Inc., L=Herndon, S=Virginia, C=US" }; - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private Timer _updateTimer = new Timer(); - private Timer openzitiRemovalTimer = new Timer(); private bool inUpdateCheck = false; private string exeLocation = null; diff --git a/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs index 65c1af82..0aeb81b2 100644 --- a/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs +++ b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs @@ -28,6 +28,7 @@ namespace ZitiUpdateService.Checkers.PeFile { private readonly X509Certificate2 expectedRootCa = null; private readonly X509Store empty = new X509Store(); private X509Store SystemCAs = new X509Store(); + private const string oldKnownThumbprint = "39636E9F5E80308DE370C914CE8112876ECF4E0C"; public SignedFileValidator(string pathToFile) { X509Store store = new X509Store("MY", StoreLocation.CurrentUser); @@ -144,8 +145,8 @@ namespace ZitiUpdateService.Checkers.PeFile { try { innerSignerInfo.CheckSignature(false); } - catch (CryptographicException ce) { - if (innerSignerInfo.Certificate.Thumbprint == "39636E9F5E80308DE370C914CE8112876ECF4E0C") { + catch (CryptographicException) { + if (innerSignerInfo.Certificate.Thumbprint == oldKnownThumbprint) { //special handling for the known 'old' NetFoundry signing certificate, now expired... //TODO: remove this code after 2021 //just allow this one error @@ -229,11 +230,15 @@ namespace ZitiUpdateService.Checkers.PeFile { chain.Build(new X509Certificate2(certificate)); List exceptions = new List(); foreach (X509ChainStatus status in chain.ChainStatus) { - if (status.Status == X509ChainStatusFlags.NoError || status.Status == X509ChainStatusFlags.UntrustedRoot || status.Status == X509ChainStatusFlags.NotTimeValid) { + if (status.Status == X509ChainStatusFlags.NoError || status.Status == X509ChainStatusFlags.UntrustedRoot) { //X509ChainStatusFlags.UntrustedRoot simply means it was not found in the computer/user's trust store.... that's fine... - //X509ChainStatusFlags.NotTimeValid means the certificate has expired - we're allowing this for now (summer 2021) to allow older clients to update } else { - exceptions.Add(new CryptographicException("Could not verify trust: " + status.StatusInformation)); + if (status.Status == X509ChainStatusFlags.NotTimeValid && certificate.Thumbprint == oldKnownThumbprint) { + //X509ChainStatusFlags.NotTimeValid means the certificate has expired - we're allowing this for now (summer 2021) to allow older clients to update + Logger.Warn("Executable is signed using the old signing certificate. Allowing this certificate to report as expired"); + } else { + exceptions.Add(new CryptographicException("Could not verify trust: " + status.StatusInformation)); + } } } From dad8cad74b47360b31cc70d03ca1de83595adf9e Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Thu, 3 Jun 2021 17:24:28 -0400 Subject: [PATCH 08/19] add signing of exe to build script --- Installer/build.ps1 | 8 +++++++- ZitiDesktopEdge.sln | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Installer/build.ps1 b/Installer/build.ps1 index 67c8559e..f9de8bc9 100644 --- a/Installer/build.ps1 +++ b/Installer/build.ps1 @@ -70,5 +70,11 @@ if($gituser -eq "ziti-ci") { } else { echo "detected user [${gituser}] which is not ziti-ci - skipping installer commit" } -(Get-FileHash "${scriptPath}\Output\Ziti Desktop Edge Client-${installerVersion}.exe").Hash > "${scriptPath}\Output\Ziti Desktop Edge Client-${installerVersion}.exe.sha256" + +$exeAbsPath="${scriptPath}\Output\Ziti Desktop Edge Client-${installerVersion}.exe" + +echo "adding additional signature to executable with openziti.org signing certificate" +signtool sign /f "${env:PFXFILE_OPENZITI}" /p "${env:PFXPASS_OPENZITI}" /fd sha512 /td sha512 /as "${exeAbsPath}" + +(Get-FileHash "${exeAbsPath}").Hash > "${scriptPath}\Output\Ziti Desktop Edge Client-${installerVersion}.exe.sha256" echo "========================== build.ps1 competed ==========================" diff --git a/ZitiDesktopEdge.sln b/ZitiDesktopEdge.sln index 8167e22a..85a8a8ee 100644 --- a/ZitiDesktopEdge.sln +++ b/ZitiDesktopEdge.sln @@ -6,7 +6,9 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D66B1C6A-ADFE-437E-B037-C4C849A6CA97}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + Installer\build.ps1 = Installer\build.ps1 README.md = README.md + Installer\reg.bat = Installer\reg.bat EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZitiDesktopEdge", "DesktopEdge\ZitiDesktopEdge.csproj", "{FA4291EA-A97E-4854-959F-91EE7B330B37}" From d99c8b282ef4c9c72f8058ced9b962e3519f5c47 Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Thu, 3 Jun 2021 17:35:17 -0400 Subject: [PATCH 09/19] add /tr field to signing step --- Installer/build.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Installer/build.ps1 b/Installer/build.ps1 index f9de8bc9..8643a514 100644 --- a/Installer/build.ps1 +++ b/Installer/build.ps1 @@ -74,7 +74,7 @@ if($gituser -eq "ziti-ci") { $exeAbsPath="${scriptPath}\Output\Ziti Desktop Edge Client-${installerVersion}.exe" echo "adding additional signature to executable with openziti.org signing certificate" -signtool sign /f "${env:PFXFILE_OPENZITI}" /p "${env:PFXPASS_OPENZITI}" /fd sha512 /td sha512 /as "${exeAbsPath}" +signtool sign /f "${env:PFXFILE_OPENZITI}" /p "${env:PFXPASS_OPENZITI}" /tr http://ts.ssl.com /fd sha512 /td sha512 /as "${exeAbsPath}" (Get-FileHash "${exeAbsPath}").Hash > "${scriptPath}\Output\Ziti Desktop Edge Client-${installerVersion}.exe.sha256" echo "========================== build.ps1 competed ==========================" From 7ba5264aef57ffc5c20c2ebed6eb3a8e1a3a0842 Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Thu, 3 Jun 2021 18:34:21 -0400 Subject: [PATCH 10/19] very minor warning fixes --- DesktopEdge/MainWindow.xaml.cs | 3 ++- .../ServiceClient/AbstractClient.cs | 4 ++-- .../checkers/PeFile/SignedFileValidator.cs | 22 ++++++++++--------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/DesktopEdge/MainWindow.xaml.cs b/DesktopEdge/MainWindow.xaml.cs index 3e9bcdca..a727ff65 100644 --- a/DesktopEdge/MainWindow.xaml.cs +++ b/DesktopEdge/MainWindow.xaml.cs @@ -1439,7 +1439,8 @@ namespace ZitiDesktopEdge { public bool CanExecute(object parameter) { return true; } - +#pragma warning disable CS0067 //The event 'ActionCommand.CanExecuteChanged' is never used public event EventHandler CanExecuteChanged; +#pragma warning restore CS0067 //The event 'ActionCommand.CanExecuteChanged' is never used } } diff --git a/ZitiDesktopEdge.Client/ServiceClient/AbstractClient.cs b/ZitiDesktopEdge.Client/ServiceClient/AbstractClient.cs index 4b90a28e..c5d96dca 100644 --- a/ZitiDesktopEdge.Client/ServiceClient/AbstractClient.cs +++ b/ZitiDesktopEdge.Client/ServiceClient/AbstractClient.cs @@ -165,10 +165,10 @@ namespace ZitiDesktopEdge.ServiceClient { } else { //ClientDisconnected(null); } - } catch (Exception e) { + } catch (Exception) { try { ReconnectFailureEvent("reconnect failure"); - } catch (Exception ex){ + } catch (Exception){ // don't care - just catch it and continue... it's a timeout... } var now = DateTime.Now; diff --git a/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs index 0aeb81b2..3cc94d42 100644 --- a/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs +++ b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs @@ -187,17 +187,19 @@ namespace ZitiUpdateService.Checkers.PeFile { public void Verify() { List list = ExtractVerifiedSignatureCertificates(); foreach (X509Certificate2 cert in list) { - if (IsCertificateOpenZitiVerifies(cert)) { - //verify this certificate was issued from the known CA - try { - VerifyTrust(empty, expectedRootCa, cert, true).Wait(); - Logger.Info("Certificate {0}:{1} was verified signed by {2}:{3}", cert.Thumbprint, cert.Subject, expectedRootCa.Thumbprint, expectedRootCa.Subject); - return; //yes! - } catch { - //empty on purpose. keep checking... - } - } + if (IsCertificateOpenZitiVerifies(cert)) { + //verify this certificate was issued from the known CA + try { + VerifyTrust(empty, expectedRootCa, cert, true).Wait(); + Logger.Info("Certificate {0}:{1} was verified signed by {2}:{3}", cert.Thumbprint, cert.Subject, expectedRootCa.Thumbprint, expectedRootCa.Subject); + return; //yes! + } + catch { + //empty on purpose. keep checking... + } + } } + throw new CryptographicException("Executable not signed by an appropriate certificate"); } From e2c54af6bcc7a8097a7ccb3d395c47a03044db9e Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Thu, 3 Jun 2021 18:36:17 -0400 Subject: [PATCH 11/19] cleanup based on self-PR review --- .../checkers/PeFile/SignedFileValidator.cs | 32 ------------------- service/cziti/dns-proxy.go | 3 -- 2 files changed, 35 deletions(-) diff --git a/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs index 3cc94d42..0a75f5f0 100644 --- a/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs +++ b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs @@ -518,38 +518,6 @@ namespace ZitiUpdateService.Checkers.PeFile { Pe32Plus } - public class CRLDistributionPointsParser { - public static List GetCrlUrls(BinaryReader asn1Data) { - //read the first byte - make sure it's marker 0x30 for SEQUENCE - byte first = asn1Data.ReadByte(); - if (first != 0x30) { - throw new CryptographicException("Could not parse CRL Distribution Points. First byte not asn marker"); - } - - if (first > 127) { - //means there's more bytes to read that control the overall length of the next section - } - List rtn = new List(); - - return rtn; - } - - private static void readType(MemoryStream asn1Data) { - - } - - private static void readLen(byte lenByte, MemoryStream asn1Data) { - - } - /* - internal static CrlDistributionPointParser ParseTags(BinaryReader asn1Data, int asn1DataLen) { - - CrlDistributionPointParser tag = new CrlDistributionPointParser(asn1Data, asn1DataLen); - - return tag; - }*/ - } - internal class CrlDistributionPointParser { internal int offset; internal int length; diff --git a/service/cziti/dns-proxy.go b/service/cziti/dns-proxy.go index 1e311972..94b1002a 100644 --- a/service/cziti/dns-proxy.go +++ b/service/cziti/dns-proxy.go @@ -354,7 +354,6 @@ outer: for { select { case pr := <-proxiedRequests: - log.Warnf("i am in proxiedRequests") id := (uint32(pr.req.Id) << 16) | uint32(pr.req.Question[0].Qtype) reqs[id] = pr b, _ := pr.req.Pack() @@ -377,7 +376,6 @@ outer: } case rep := <-respChan: - log.Warnf("i am in respChan") reply := dns.Msg{} if err := reply.Unpack(rep); err == nil { id := (uint32(reply.Id) << 16) | uint32(reply.Question[0].Qtype) @@ -396,7 +394,6 @@ outer: } } case <-time.After(time.Minute): - log.Warnf("i am in time.After") // cleanup requests we didn't get answers for now := time.Now() for k, r := range reqs { From 22b3f6e7c7d679fe16787b8351887e8c26896c35 Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Thu, 3 Jun 2021 18:41:05 -0400 Subject: [PATCH 12/19] continued tiny fixes based on self-PR --- ZitiUpdateService/UpdateService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ZitiUpdateService/UpdateService.cs b/ZitiUpdateService/UpdateService.cs index 3ea20548..debe9e45 100644 --- a/ZitiUpdateService/UpdateService.cs +++ b/ZitiUpdateService/UpdateService.cs @@ -638,8 +638,8 @@ namespace ZitiUpdateService { return; } Logger.Debug("downloaded file hash was correct. update can continue."); - - new SignedFileValidator(fileDestination).Verify(); + + new SignedFileValidator(fileDestination).Verify(); StopZiti(); StopUI().Wait(); From 75686c2084901d10c67280277cd9d8e635c34a15 Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Thu, 3 Jun 2021 19:04:43 -0400 Subject: [PATCH 13/19] small cleanup. update github action with new build material needs --- .github/workflows/installer.build.yml | 6 +++++- Installer/build.ps1 | 2 +- ZitiDesktopEdge.sln | 1 + ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs | 6 +++++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/installer.build.yml b/.github/workflows/installer.build.yml index a08c08fa..a6acf427 100644 --- a/.github/workflows/installer.build.yml +++ b/.github/workflows/installer.build.yml @@ -22,7 +22,9 @@ jobs: JFROG_ACCESS_TOKEN: ${{ secrets.JFROG_ACCESS_TOKEN }} gh_ci_key: ${{ secrets.GH_CI_KEY }} ZITI_DEBUG: "yes_please" - + OPENZITI_P12_PASS: ${{ secrets.OPENZITI_P12_PASS }} + OPENZITI_P12: ${{ secrets.OPENZITI_P12 }} + steps: - uses: actions/checkout@master - uses: microsoft/setup-msbuild@v1 @@ -39,7 +41,9 @@ jobs: run: | echo "${env:SIGNING_CERT}" > WinSign.b64 echo "${env:ADV_INST_KEY}" > ADV_INST_KEY.key + echo "${env:OPENZITI_P12}" > OPENZITI_P12.b64 C:\Windows\System32\certutil.exe -decode WinSign.b64 Installer/WinSign.p12 + C:\Windows\System32\certutil.exe -decode OPENZITI_P12.b64 Installer/openziti.p12 - name: Trying to run choco install. run: Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) - name: "Install Choco needs: advanced-installer wixtoolset zip ninja" diff --git a/Installer/build.ps1 b/Installer/build.ps1 index 8643a514..e0e51091 100644 --- a/Installer/build.ps1 +++ b/Installer/build.ps1 @@ -74,7 +74,7 @@ if($gituser -eq "ziti-ci") { $exeAbsPath="${scriptPath}\Output\Ziti Desktop Edge Client-${installerVersion}.exe" echo "adding additional signature to executable with openziti.org signing certificate" -signtool sign /f "${env:PFXFILE_OPENZITI}" /p "${env:PFXPASS_OPENZITI}" /tr http://ts.ssl.com /fd sha512 /td sha512 /as "${exeAbsPath}" +signtool sign /f "${scriptPath}\openziti.p12" /p "${env:OPENZITI_P12_PASS}" /tr http://ts.ssl.com /fd sha512 /td sha512 /as "${exeAbsPath}" (Get-FileHash "${exeAbsPath}").Hash > "${scriptPath}\Output\Ziti Desktop Edge Client-${installerVersion}.exe.sha256" echo "========================== build.ps1 competed ==========================" diff --git a/ZitiDesktopEdge.sln b/ZitiDesktopEdge.sln index 85a8a8ee..70b973ed 100644 --- a/ZitiDesktopEdge.sln +++ b/ZitiDesktopEdge.sln @@ -7,6 +7,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig Installer\build.ps1 = Installer\build.ps1 + .github\workflows\installer.build.yml = .github\workflows\installer.build.yml README.md = README.md Installer\reg.bat = Installer\reg.bat EndProjectSection diff --git a/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs index 0a75f5f0..cb4ac576 100644 --- a/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs +++ b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs @@ -185,13 +185,17 @@ namespace ZitiUpdateService.Checkers.PeFile { } public void Verify() { + Logger.Debug("extracting certificates from file: {0}", FilePath); List list = ExtractVerifiedSignatureCertificates(); + Logger.Info("Certificates extracted: {0}", list.Count); foreach (X509Certificate2 cert in list) { + Logger.Info("Checking certificate: [{0}] {1}", cert.Thumbprint, cert); if (IsCertificateOpenZitiVerifies(cert)) { //verify this certificate was issued from the known CA try { + Logger.Info("Verifying trust of certificate: [{0}] {1}", cert, cert.Thumbprint); VerifyTrust(empty, expectedRootCa, cert, true).Wait(); - Logger.Info("Certificate {0}:{1} was verified signed by {2}:{3}", cert.Thumbprint, cert.Subject, expectedRootCa.Thumbprint, expectedRootCa.Subject); + Logger.Info("Certificate [{0}] {1} was verified signed by [{2}] {3}", cert.Thumbprint, cert.Subject, expectedRootCa.Thumbprint, expectedRootCa.Subject); return; //yes! } catch { From b856254bdb5d86448e245415cca88c2b7cfd2819 Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Thu, 3 Jun 2021 19:06:53 -0400 Subject: [PATCH 14/19] this branch should be 1.9.7 not 1.9.8 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 66beabb5..fee0a278 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.9.8 +1.9.7 From e47e9362333d765de2745913aecdd4aae11f96b9 Mon Sep 17 00:00:00 2001 From: Mary Lincy Dcouto Date: Fri, 4 Jun 2021 16:34:12 +0530 Subject: [PATCH 15/19] updating t-sdk 0.15.7 --- release-notes.md | 14 ++++++++++++++ service/build.bat | 2 +- version | 3 ++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/release-notes.md b/release-notes.md index 611c4a40..0a509425 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,3 +1,17 @@ +# Release 1.9.7 + +## What's New +* none + +## Other changes: +* none + +## Bugs fixed: +* none + +## Dependency Updates +* tsdk is updated to v0.15.7, c-sdk 0.23.3 + # Release 1.9.6 ## What's New diff --git a/service/build.bat b/service/build.bat index e1b6b27a..3c390e0c 100644 --- a/service/build.bat +++ b/service/build.bat @@ -14,7 +14,7 @@ REM See the License for the specific language governing permissions and REM limitations under the License. REM SET REPO_URL=https://github.com/openziti/ziti-tunnel-sdk-c.git -SET ZITI_TUNNEL_REPO_BRANCH=v0.15.6 +SET ZITI_TUNNEL_REPO_BRANCH=v0.15.7 REM override the c sdk used in the build - leave blank for the same as specified in the tunneler sdk SET ZITI_SDK_C_BRANCH= REM the number of TCP connections the tunneler sdk can have at any one time diff --git a/version b/version index 7bc1c404..d2435e46 100644 --- a/version +++ b/version @@ -1 +1,2 @@ -1.9.6 + +1.9.7 From 6b51e19cdb2bc48678ca1a08d183cd6868db33ba Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Fri, 4 Jun 2021 12:01:36 -0400 Subject: [PATCH 16/19] use 1.9.8 for the version and cleanup silly typo in a function name --- ZitiUpdateService/checkers/PeFile/Win32Crypto.cs | 4 ++-- version | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ZitiUpdateService/checkers/PeFile/Win32Crypto.cs b/ZitiUpdateService/checkers/PeFile/Win32Crypto.cs index a515baf6..b84dd21e 100644 --- a/ZitiUpdateService/checkers/PeFile/Win32Crypto.cs +++ b/ZitiUpdateService/checkers/PeFile/Win32Crypto.cs @@ -159,7 +159,7 @@ namespace VerifyingFiles.PInvoke { #endregion - private static List GetRevokedGetSerialNumbers(CRL_INFO stCrlInfo) { + private static List GetRevokedSerialNumbers(CRL_INFO stCrlInfo) { List rtn = new List(); var rgCrlEntry = stCrlInfo.rgCRLEntry; @@ -228,7 +228,7 @@ namespace VerifyingFiles.PInvoke { CrlInfo info = new CrlInfo(); info.validTo = FileTimeToDateTime(stCrlInfo.NextUpdate); info.validFrom = FileTimeToDateTime(stCrlInfo.ThisUpdate); - info.RevokedSerialNumbers = GetRevokedGetSerialNumbers(stCrlInfo); + info.RevokedSerialNumbers = GetRevokedSerialNumbers(stCrlInfo); return info; } finally { diff --git a/version b/version index fee0a278..66beabb5 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.9.7 +1.9.8 From ee3ae6e2d5bb9521ea0ed8d3f1f1c348e8bc781f Mon Sep 17 00:00:00 2001 From: Mary Lincy Dcouto Date: Fri, 4 Jun 2021 22:53:30 +0530 Subject: [PATCH 17/19] removing unnecessary space --- version | 1 - 1 file changed, 1 deletion(-) diff --git a/version b/version index d2435e46..fee0a278 100644 --- a/version +++ b/version @@ -1,2 +1 @@ - 1.9.7 From 640510da9d7478ff0e4d9c5c95d1a3df504e949a Mon Sep 17 00:00:00 2001 From: ziti-ci Date: Fri, 4 Jun 2021 17:33:18 +0000 Subject: [PATCH 18/19] [ci skip] committing updated version related files --- DesktopEdge/Properties/AssemblyInfo.cs | 6 +++--- Installer/ZitiDesktopEdge.aip | 4 ++-- ZitiUpdateService/Properties/AssemblyInfo.cs | 6 +++--- service/ziti-tunnel/version.go | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/DesktopEdge/Properties/AssemblyInfo.cs b/DesktopEdge/Properties/AssemblyInfo.cs index 69c6b337..7ae01ac7 100644 --- a/DesktopEdge/Properties/AssemblyInfo.cs +++ b/DesktopEdge/Properties/AssemblyInfo.cs @@ -50,6 +50,6 @@ using System.Windows; // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.9.6.0")] -[assembly: AssemblyVersion("1.9.6.0")] -[assembly: AssemblyFileVersion("1.9.6.0")] +// [assembly: AssemblyVersion("1.9.7.0")] +[assembly: AssemblyVersion("1.9.7.0")] +[assembly: AssemblyFileVersion("1.9.7.0")] diff --git a/Installer/ZitiDesktopEdge.aip b/Installer/ZitiDesktopEdge.aip index 891569b9..af774c59 100644 --- a/Installer/ZitiDesktopEdge.aip +++ b/Installer/ZitiDesktopEdge.aip @@ -23,10 +23,10 @@ - + - + diff --git a/ZitiUpdateService/Properties/AssemblyInfo.cs b/ZitiUpdateService/Properties/AssemblyInfo.cs index 03a5efb4..40bb1b2b 100644 --- a/ZitiUpdateService/Properties/AssemblyInfo.cs +++ b/ZitiUpdateService/Properties/AssemblyInfo.cs @@ -30,6 +30,6 @@ using System.Runtime.InteropServices; // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.9.6.0")] -[assembly: AssemblyVersion("1.9.6.0")] -[assembly: AssemblyFileVersion("1.9.6.0")] +// [assembly: AssemblyVersion("1.9.7.0")] +[assembly: AssemblyVersion("1.9.7.0")] +[assembly: AssemblyFileVersion("1.9.7.0")] diff --git a/service/ziti-tunnel/version.go b/service/ziti-tunnel/version.go index 5305d2a9..e7912fd6 100644 --- a/service/ziti-tunnel/version.go +++ b/service/ziti-tunnel/version.go @@ -20,9 +20,9 @@ package main const ( - Version = "1.9.6" - Revision = "ed9a214d5c33" - Branch = "main" + Version = "1.9.7" + Revision = "ee3ae6e2d5bb" + Branch = "release-next" BuildUser = "" - BuildDate = "2021-05-31 14:36:25" + BuildDate = "2021-06-04 17:27:26" ) From df3de1f0ee8fcddefd178eb9bbc508cf6b3a3052 Mon Sep 17 00:00:00 2001 From: ziti-ci Date: Mon, 7 Jun 2021 18:31:36 +0000 Subject: [PATCH 19/19] [ci skip] committing updated version related files --- DesktopEdge/Properties/AssemblyInfo.cs | 6 +++--- ZitiUpdateService/Properties/AssemblyInfo.cs | 6 +++--- service/ziti-tunnel/version.go | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/DesktopEdge/Properties/AssemblyInfo.cs b/DesktopEdge/Properties/AssemblyInfo.cs index 7ae01ac7..a9be57de 100644 --- a/DesktopEdge/Properties/AssemblyInfo.cs +++ b/DesktopEdge/Properties/AssemblyInfo.cs @@ -50,6 +50,6 @@ using System.Windows; // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.9.7.0")] -[assembly: AssemblyVersion("1.9.7.0")] -[assembly: AssemblyFileVersion("1.9.7.0")] +// [assembly: AssemblyVersion("1.9.8.0")] +[assembly: AssemblyVersion("1.9.8.0")] +[assembly: AssemblyFileVersion("1.9.8.0")] diff --git a/ZitiUpdateService/Properties/AssemblyInfo.cs b/ZitiUpdateService/Properties/AssemblyInfo.cs index c384a42b..54e00a6d 100644 --- a/ZitiUpdateService/Properties/AssemblyInfo.cs +++ b/ZitiUpdateService/Properties/AssemblyInfo.cs @@ -30,6 +30,6 @@ using System.Runtime.InteropServices; // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.9.6.0")] -[assembly: AssemblyVersion("1.9.4.0")] -[assembly: AssemblyFileVersion("1.9.4.0")] +// [assembly: AssemblyVersion("1.9.8.0")] +[assembly: AssemblyVersion("1.9.8.0")] +[assembly: AssemblyFileVersion("1.9.8.0")] diff --git a/service/ziti-tunnel/version.go b/service/ziti-tunnel/version.go index e7912fd6..2307c033 100644 --- a/service/ziti-tunnel/version.go +++ b/service/ziti-tunnel/version.go @@ -20,9 +20,9 @@ package main const ( - Version = "1.9.7" - Revision = "ee3ae6e2d5bb" - Branch = "release-next" + Version = "1.9.8" + Revision = "a763f2acac36" + Branch = "main" BuildUser = "" - BuildDate = "2021-06-04 17:27:26" + BuildDate = "2021-06-07 18:26:10" )