Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a downloader + verifier for static.rust-lang.org #179

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions VisualRust.Setup/VisualRust.Setup.wixproj
Expand Up @@ -38,6 +38,8 @@
<IntermediateOutputPath>obj\$(Platform)\$(Configuration)\</IntermediateOutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="gpg2013.wxs" />
<Compile Include="gpg2015.wxs" />
<Compile Include="gdb\gdb.2013.x86_64.wxs" />
<Compile Include="gdb\gdb.2015.x86_64.wxs" />
<Compile Include="gdb\gdb.2015.i686.wxs" />
Expand Down
35 changes: 35 additions & 0 deletions VisualRust.Setup/gpg2013.wxs
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?define VsVersion = "2013"?>
<Fragment>
<DirectoryRef Id="Dir_vsx_$(var.VsVersion)">
<Directory Id="Dir_gpg_$(var.VsVersion)" Name="gpg"/>
</DirectoryRef>
<ComponentGroup Id="CmpGroup_gpg_$(var.VsVersion)" Directory="Dir_gpg_$(var.VsVersion)">
<Component Id="Cmp_gpg2_exe_$(var.VsVersion)" Guid="{0E7D8C96-2B04-418B-807D-151B0CFDC5B8}">
<File Id="File_gpg2_exe_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\gpg2.exe" />
</Component>
<Component Id="Cmp_gpgconf_exe_$(var.VsVersion)" Guid="{DC9B0D68-F3C0-4705-AEF8-5E4A35C12F52}">
<File Id="File_gpgconf_exe_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\gpgconf.exe" />
</Component>
<Component Id="Cmp_libadns_1_dll_$(var.VsVersion)" Guid="{C9430CDF-2EA8-43B7-81E4-4250465AC83D}">
<File Id="File_libadns_1_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libadns-1.dll" />
</Component>
<Component Id="Cmp_libassuan_0_dll_$(var.VsVersion)" Guid="{0EE3029A-94C7-4272-B6E4-EC10BA3B6841}">
<File Id="File_libassuan_0_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libassuan-0.dll" />
</Component>
<Component Id="Cmp_libgcrypt_20_dll_$(var.VsVersion)" Guid="{16983C76-7A7C-4AEE-84AB-B45C3FECBC82}">
<File Id="File_libgcrypt_20_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libgcrypt-20.dll" />
</Component>
<Component Id="Cmp_libgpg_error_0_dll_$(var.VsVersion)" Guid="{2ED73457-0617-4D5F-A77E-61280BDCB46A}">
<File Id="File_libgpg_error_0_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libgpg-error-0.dll" />
</Component>
<Component Id="Cmp_libiconv_2_dll_$(var.VsVersion)" Guid="{215FBBA9-BBD5-4DAA-B380-99F6F79C9075}">
<File Id="File_libiconv_2_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libiconv-2.dll" />
</Component>
<Component Id="Cmp_zlib1_dll_$(var.VsVersion)" Guid="{11231EF6-57F8-48CB-9224-5E3878D6A988}">
<File Id="File_zlib1_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\zlib1.dll" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>
35 changes: 35 additions & 0 deletions VisualRust.Setup/gpg2015.wxs
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?define VsVersion = "2015"?>
<Fragment>
<DirectoryRef Id="Dir_vsx_$(var.VsVersion)">
<Directory Id="Dir_gpg_$(var.VsVersion)" Name="gpg"/>
</DirectoryRef>
<ComponentGroup Id="CmpGroup_gpg_$(var.VsVersion)" Directory="Dir_gpg_$(var.VsVersion)">
<Component Id="Cmp_gpg2_exe_$(var.VsVersion)" Guid="{70DE99AA-2F68-44FA-8CAF-F88618ACCFE7}">
<File Id="File_gpg2_exe_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\gpg2.exe" />
</Component>
<Component Id="Cmp_gpgconf_exe_$(var.VsVersion)" Guid="{98CDD029-DBC0-414C-AAED-7EC8A5493FBB}">
<File Id="File_gpgconf_exe_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\gpgconf.exe" />
</Component>
<Component Id="Cmp_libadns_1_dll_$(var.VsVersion)" Guid="{ffa52ef1-1d46-4b71-92c8-f8ec75102362}">
<File Id="File_libadns_1_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libadns-1.dll" />
</Component>
<Component Id="Cmp_libassuan_0_dll_$(var.VsVersion)" Guid="{aaabf11a-e79f-4e82-82f0-b52c4539a7a9}">
<File Id="File_libassuan_0_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libassuan-0.dll" />
</Component>
<Component Id="Cmp_libgcrypt_20_dll_$(var.VsVersion)" Guid="{fa5e46c8-2179-481b-89c6-adef01e4a186}">
<File Id="File_libgcrypt_20_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libgcrypt-20.dll" />
</Component>
<Component Id="Cmp_libgpg_error_0_dll_$(var.VsVersion)" Guid="{91ecb85a-8178-4930-aed1-8d35d9bf19d4}">
<File Id="File_libgpg_error_0_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libgpg-error-0.dll" />
</Component>
<Component Id="Cmp_libiconv_2_dll_$(var.VsVersion)" Guid="{e59eb84a-278a-4c9f-8c4a-fca4b4d7a66a}">
<File Id="File_libiconv_2_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\libiconv-2.dll" />
</Component>
<Component Id="Cmp_zlib1_dll_$(var.VsVersion)" Guid="{77471145-bd88-4d86-a76e-d5a29789b1a1}">
<File Id="File_zlib1_dll_$(var.VsVersion)" KeyPath="yes" Source="$(var.VisualRust.TargetDir)\gpg\zlib1.dll" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>
2 changes: 2 additions & 0 deletions VisualRust.Setup/vsx2013.wxs
Expand Up @@ -37,6 +37,8 @@
</Component>
<!-- Racer files -->
<ComponentGroupRef Id="CmpGroup_Racer_2013"/>
<!-- GPG files -->
<ComponentGroupRef Id="CmpGroup_gpg_2013"/>
<!-- Open .rsproj with vs launcher -->
<Component Id="Cmp_RsprojRegistration_$(var.VsVersion)" Guid="{15CB1600-9413-4770-9BDC-39C8593B8FC4}">
<CreateFolder/>
Expand Down
2 changes: 2 additions & 0 deletions VisualRust.Setup/vsx2015.wxs
Expand Up @@ -37,6 +37,8 @@
</Component>
<!-- Racer files -->
<ComponentGroupRef Id="CmpGroup_Racer_2015"/>
<!-- GPG file -->
<ComponentGroupRef Id="CmpGroup_gpg_2015"/>
<!-- Open .rsproj with vs launcher -->
<Component Id="Cmp_RsprojRegistration_$(var.VsVersion)" Guid="{D78325B7-966F-44C1-A8ED-2074C4EDC106}">
<CreateFolder/>
Expand Down
102 changes: 102 additions & 0 deletions VisualRust/Downloader.cs
@@ -0,0 +1,102 @@
using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Runtime.Serialization;

namespace VisualRust
{
public class Downloader
{
private static String[] Sha256Fingerprints =
{
"0C:0B:AC:4C:96:D9:F2:2C:8D:7A:00:9F:2F:48:3D:7B:46:FE:2C:60:0B:52:19:5B:B4:80:47:36:7C:03:E9:41",
// all the known static.rust-lang.org cert fingerprints should be specified here. they expire every
// 2 years.
};

private static bool ValidateCertificate(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors != SslPolicyErrors.None)
{
return false;
}

var cert2 = new X509Certificate2(certificate);
var time = System.DateTime.Now;

if (time > cert2.NotAfter || time < cert2.NotBefore)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure about this check? I thought this was covered by default checks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not, was just being safe. Where are the default checks?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find explicit documentation for it, but I see failures on common errors (self-signed: https://www.pcwebshop.co.uk/, wrong domain: https://tv.eurosport.com/, expired: https://testssl-expire.disig.sk/index.en.html). You will be getting sslPolicyErrors != SslPolicyErrors.None inside the callback in all those cases. This expiration check does nothing.

{
// expiry
return false;
}

var der_encoded = certificate.Export(X509ContentType.Cert);
var hash = SHA256.Create().ComputeHash(der_encoded);
var received_fingerprint = BitConverter.ToString(hash).Replace('-', ':');

foreach (String fingerprint in Sha256Fingerprints)
{
if (fingerprint == received_fingerprint) { return true; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a security person by any stretch of the imagination, so this might be dumb, but what does checking for exact cert fingerprint gives us?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It gives us the knowledge that the connection isn't being MITM'd by any other certificates that may be trusted by the root CA store - either added by malware, compromised certificate, or just plain malice (which has been documented)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's called certificate pinning, and browsers use it in the form of HSTS.

}

return false;
}

private static WebResponse RawDownload(String relative_path)
{
HttpWebRequest wc = (HttpWebRequest)WebRequest.Create("https://static.rust-lang.org/dist/" + relative_path);
wc.ServerCertificateValidationCallback = ValidateCertificate;
return wc.GetResponse();
}

/// <summary>
/// Download and verify the signature of a file rust-lang.org, writing the contents into write_into.
/// </summary>
/// <exception cref="VerificationException">
/// Thrown when the signature of the downloaded file could not be verified with Rust's signing key.
/// Note that the stream will still contain the contents of the file even if verification failed. But,
/// if verification fails, the contents should not be trusted.
/// </exception>
/// <param name="relative_path">Appended to https://static.rust-lang.org/dist/ to determine the file to download</param>
/// <param name="write_into">The contents of the downloaded file (but not the signature) will be written into this stream.</param>
public static void Download(String relative_path, Stream write_into)
{
var saved_pos = write_into.Position;
MemoryStream sig = new MemoryStream();
RawDownload(relative_path).GetResponseStream().CopyTo(write_into);
RawDownload(relative_path + ".asc").GetResponseStream().CopyTo(sig);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WebResponse returned by RawDownload are IDisposable and must be disposed (preferably with using).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I've been pretty sloppy with the IDisposable overall.

write_into.Position = 0;
sig.Position = 0;
var res = GPG.Gpg.Instance.Verify(write_into, sig);
if (!res.Item1)
{
File.WriteAllText("C:/Users/Elaine/Desktop/gpg_log.txt", res.Item2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You forgot something! You might consider Utils.DebugPrintToOutput(...) or Utils.PrintToOutput(...) instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, I did :) Thanks for the hint, I'll use those instead.

throw new VerificationException(res.Item2);
}
}
}

[Serializable]
public class VerificationException : Exception
{
public VerificationException()
{
}

public VerificationException(string message) : base(message)
{
}

public VerificationException(string message, Exception innerException) : base(message, innerException)
{
}

protected VerificationException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}
6 changes: 6 additions & 0 deletions VisualRust/Forms/RustOptionsPage.cs
Expand Up @@ -15,6 +15,12 @@ public class RustOptionsPage : DialogPage
public bool UseCustomSources { get; set; }
public string CustomSourcesPath { get; set; }

public bool UseCustomGpg { get; set; }
public string CustomGpgPath { get; set; }

public bool UseCustomGpgHomedir { get; set; }
public string CustomGpgHomedir { get; set; }

private RustOptionsPageControl _page;

protected override IWin32Window Window
Expand Down
37 changes: 5 additions & 32 deletions VisualRust/Racer/RacerSingleton.cs
Expand Up @@ -51,23 +51,18 @@ public static void Init()
private RacerSingleton()
{
}

private T GetVisualRustProperty<T>(DTE env, string key)
{
return (T)env.get_Properties("Visual Rust", "General").Item(key).Value;
}


private void ReinitializeRacerPaths()
{
DTE env = (DTE)VisualRustPackage.GetGlobalService(typeof(DTE));
// If path to racer.exe is specifed, use it
if(GetVisualRustProperty<bool>(env, "UseCustomRacer"))
racerPathForExecution = GetVisualRustProperty<string>(env, "CustomRacerPath");
if(Utils.GetVisualRustProperty<bool>(env, "UseCustomRacer"))
racerPathForExecution = Utils.GetVisualRustProperty<string>(env, "CustomRacerPath");
else
racerPathForExecution = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Racer", BundledRacerExecutable);
// Same for custom RUST_SRC_PATH
if(GetVisualRustProperty<bool>(env, "UseCustomSources"))
racerSourcesLocation = GetVisualRustProperty<string>(env, "CustomSourcesPath");
if(Utils.GetVisualRustProperty<bool>(env, "UseCustomSources"))
racerSourcesLocation = Utils.GetVisualRustProperty<string>(env, "CustomSourcesPath");
else
racerSourcesLocation = null;
}
Expand Down Expand Up @@ -136,27 +131,5 @@ private string Exec(string args)
return "";
}
}

class WindowsErrorMode : IDisposable
{
[DllImport("kernel32.dll", SetLastError = true)]
private static extern int SetErrorMode(int wMode);

private readonly int oldErrorMode;

/// <summary>
/// Creates a new error mode context.
/// </summary>
/// <param name="mode">Error mode to use. 3 is a useful value.</param>
public WindowsErrorMode(int mode)
{
oldErrorMode = SetErrorMode(mode);
}

public void Dispose()
{
SetErrorMode(oldErrorMode);
}
}
}
}
28 changes: 28 additions & 0 deletions VisualRust/Utils.cs
Expand Up @@ -17,6 +17,8 @@ namespace VisualRust
using RustLexer;
using Microsoft.VisualStudio.Text;
using Antlr4.Runtime;
using System.Runtime.InteropServices;
using EnvDTE;

static class Utils
{
Expand Down Expand Up @@ -293,6 +295,11 @@ internal static void PrintToOutput(string s, params object[] args)

return Tuple.Create(leftToken, currentToken);
}

public static T GetVisualRustProperty<T>(DTE env, string key)
{
return (T)env.get_Properties("Visual Rust", "General").Item(key).Value;
}
}

public class TemporaryFile : IDisposable
Expand Down Expand Up @@ -327,4 +334,25 @@ public void Dispose()
}
}

public class WindowsErrorMode : IDisposable
{
[DllImport("kernel32.dll", SetLastError = true)]
private static extern int SetErrorMode(int wMode);

private readonly int oldErrorMode;

/// <summary>
/// Creates a new error mode context.
/// </summary>
/// <param name="mode">Error mode to use. 3 is a useful value.</param>
public WindowsErrorMode(int mode)
{
oldErrorMode = SetErrorMode(mode);
}

public void Dispose()
{
SetErrorMode(oldErrorMode);
}
}
}
34 changes: 34 additions & 0 deletions VisualRust/VisualRust.csproj
Expand Up @@ -212,6 +212,8 @@
</COMReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Downloader.cs" />
<Compile Include="gpg\Gpg.cs" />
<Compile Include="ProjectForms\BasePropertyPage.cs" />
<Compile Include="ProjectForms\BuildPropertyPage.cs" />
<Compile Include="ProjectForms\DebugPropertyPage.cs" />
Expand Down Expand Up @@ -289,6 +291,38 @@
<Link>LICENSE.txt</Link>
<IncludeInVSIX>true</IncludeInVSIX>
</Content>
<Content Include="gpg\gpg2.exe">
<IncludeInVSIX>true</IncludeInVSIX>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="gpg\gpgconf.exe">
<IncludeInVSIX>true</IncludeInVSIX>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="gpg\libadns-1.dll">
<IncludeInVSIX>true</IncludeInVSIX>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="gpg\libassuan-0.dll">
<IncludeInVSIX>true</IncludeInVSIX>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="gpg\libgcrypt-20.dll">
<IncludeInVSIX>true</IncludeInVSIX>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="gpg\libgpg-error-0.dll">
<IncludeInVSIX>true</IncludeInVSIX>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="gpg\libiconv-2.dll">
<IncludeInVSIX>true</IncludeInVSIX>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="gpg\zlib1.dll">
<IncludeInVSIX>true</IncludeInVSIX>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Racer\libgcc_s_dw2-1.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<IncludeInVSIX>true</IncludeInVSIX>
Expand Down
1 change: 1 addition & 0 deletions VisualRust/VisualRustPackage.cs
Expand Up @@ -101,6 +101,7 @@ protected override void Initialize()
docEventsListener = new RunningDocTableEventsListener((IVsRunningDocumentTable)GetService(typeof(SVsRunningDocumentTable)));

Racer.RacerSingleton.Init();
GPG.Gpg.Init();
}

protected override void Dispose(bool disposing)
Expand Down