Skip to content

Commit 34793f0

Browse files
committed
chore: enable tunnel binary verification
1 parent e1d9774 commit 34793f0

File tree

4 files changed

+69
-17
lines changed

4 files changed

+69
-17
lines changed

Vpn.Service/Downloader.cs

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,22 +78,47 @@ public async Task ValidateAsync(string path, CancellationToken ct = default)
7878

7979
public class AssemblyVersionDownloadValidator : IDownloadValidator
8080
{
81-
private readonly string _expectedAssemblyVersion;
81+
private readonly int _expectedMajor;
82+
private readonly int _expectedMinor;
83+
private readonly int _expectedBuild;
84+
private readonly int _expectedRevision;
85+
86+
private readonly Version _expectedVersion;
8287

8388
// ReSharper disable once ConvertToPrimaryConstructor
84-
public AssemblyVersionDownloadValidator(string expectedAssemblyVersion)
89+
public AssemblyVersionDownloadValidator(int expectedMajor, int expectedMinor, int expectedBuild,
90+
int expectedRevision)
8591
{
86-
_expectedAssemblyVersion = expectedAssemblyVersion;
92+
_expectedMajor = expectedMajor;
93+
_expectedMinor = expectedMinor;
94+
_expectedBuild = expectedBuild < 0 ? -1 : expectedBuild;
95+
_expectedRevision = expectedRevision < 0 ? -1 : expectedRevision;
96+
if (_expectedBuild == -1 && _expectedRevision != -1)
97+
throw new ArgumentException("Build must be set if Revision is set", nameof(expectedRevision));
98+
99+
if (_expectedBuild == -1)
100+
_expectedVersion = new Version(_expectedMajor, _expectedMinor);
101+
else if (_expectedRevision == -1)
102+
_expectedVersion = new Version(_expectedMajor, _expectedMinor, _expectedBuild);
103+
else
104+
_expectedVersion = new Version(_expectedMajor, _expectedMinor, _expectedBuild, _expectedRevision);
87105
}
88106

89107
public Task ValidateAsync(string path, CancellationToken ct = default)
90108
{
91109
var info = FileVersionInfo.GetVersionInfo(path);
92110
if (string.IsNullOrEmpty(info.ProductVersion))
93111
throw new Exception("File ProductVersion is empty or null, was the binary compiled correctly?");
94-
if (info.ProductVersion != _expectedAssemblyVersion)
112+
if (!Version.TryParse(info.ProductVersion, out var productVersion))
113+
throw new Exception($"File ProductVersion '{info.ProductVersion}' is not a valid version string");
114+
115+
// If the build or revision are -1 on the expected version, they are ignored.
116+
if (productVersion.Major != _expectedMajor || productVersion.Minor != _expectedMinor ||
117+
(_expectedBuild != -1 && productVersion.Build != _expectedBuild) ||
118+
(_expectedRevision != -1 && productVersion.Revision != _expectedRevision))
95119
throw new Exception(
96-
$"File ProductVersion is '{info.ProductVersion}', but expected '{_expectedAssemblyVersion}'");
120+
$"File ProductVersion is '{info.ProductVersion}', but expected '{_expectedVersion}'");
121+
97122
return Task.CompletedTask;
98123
}
99124
}
@@ -103,19 +128,24 @@ public Task ValidateAsync(string path, CancellationToken ct = default)
103128
/// </summary>
104129
public class CombinationDownloadValidator : IDownloadValidator
105130
{
106-
private readonly IDownloadValidator[] _validators;
131+
private readonly List<IDownloadValidator> _validators;
107132

108133
/// <param name="validators">Validators to run</param>
109134
public CombinationDownloadValidator(params IDownloadValidator[] validators)
110135
{
111-
_validators = validators;
136+
_validators = validators.ToList();
112137
}
113138

114139
public async Task ValidateAsync(string path, CancellationToken ct = default)
115140
{
116141
foreach (var validator in _validators)
117142
await validator.ValidateAsync(path, ct);
118143
}
144+
145+
public void Add(IDownloadValidator validator)
146+
{
147+
_validators.Add(validator);
148+
}
119149
}
120150

121151
/// <summary>

Vpn.Service/Manager.cs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -417,15 +417,30 @@ private async Task DownloadTunnelBinaryAsync(string baseUrl, SemVersion expected
417417
_logger.LogInformation("Downloading VPN binary from '{url}' to '{DestinationPath}'", url,
418418
_config.TunnelBinaryPath);
419419
var req = new HttpRequestMessage(HttpMethod.Get, url);
420-
var validators = new NullDownloadValidator();
421-
// TODO: re-enable when the binaries are signed and have versions
422-
/*
423-
var validators = new CombinationDownloadValidator(
424-
AuthenticodeDownloadValidator.Coder,
425-
new AssemblyVersionDownloadValidator(
426-
$"{expectedVersion.Major}.{expectedVersion.Minor}.{expectedVersion.Patch}.0")
427-
);
428-
*/
420+
421+
var validators = new CombinationDownloadValidator();
422+
if (!string.IsNullOrEmpty(_config.TunnelBinarySignatureSigner))
423+
{
424+
_logger.LogDebug("Adding Authenticode signature validator for signer '{Signer}'",
425+
_config.TunnelBinarySignatureSigner);
426+
validators.Add(new AuthenticodeDownloadValidator(_config.TunnelBinarySignatureSigner));
427+
}
428+
else
429+
{
430+
_logger.LogDebug("Skipping Authenticode signature validation");
431+
}
432+
433+
if (!_config.TunnelBinaryAllowVersionMismatch)
434+
{
435+
_logger.LogDebug("Adding version validator for version '{ExpectedVersion}'", expectedVersion);
436+
validators.Add(new AssemblyVersionDownloadValidator((int)expectedVersion.Major, (int)expectedVersion.Minor,
437+
(int)expectedVersion.Patch, -1));
438+
}
439+
else
440+
{
441+
_logger.LogDebug("Skipping tunnel binary version validation");
442+
}
443+
429444
var downloadTask = await _downloader.StartDownloadAsync(req, _config.TunnelBinaryPath, validators, ct);
430445

431446
// TODO: monitor and report progress when we have a mechanism to do so

Vpn.Service/ManagerConfig.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,9 @@ public class ManagerConfig
1111
[Required] public string TunnelBinaryPath { get; set; } = @"C:\coder-vpn.exe";
1212

1313
[Required] public string LogFileLocation { get; set; } = @"C:\coder-desktop-service.log";
14+
15+
// If empty, signatures will not be verified.
16+
[Required] public string TunnelBinarySignatureSigner { get; set; } = "Coder Technologies Inc.";
17+
18+
[Required] public bool TunnelBinaryAllowVersionMismatch { get; set; } = false;
1419
}

Vpn.Service/Program.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ public static class Program
1010
{
1111
#if !DEBUG
1212
private const string ServiceName = "Coder Desktop";
13+
private const string ManagerConfigSection = "Manager";
1314
#else
1415
private const string ServiceName = "Coder Desktop (Debug)";
16+
private const string ManagerConfigSection = "DebugManager";
1517
#endif
1618

1719
private const string ConsoleOutputTemplate =
@@ -61,7 +63,7 @@ private static async Task BuildAndRun(string[] args)
6163

6264
// Options types (these get registered as IOptions<T> singletons)
6365
builder.Services.AddOptions<ManagerConfig>()
64-
.Bind(builder.Configuration.GetSection("Manager"))
66+
.Bind(builder.Configuration.GetSection(ManagerConfigSection))
6567
.ValidateDataAnnotations()
6668
.PostConfigure(config =>
6769
{

0 commit comments

Comments
 (0)