From ca332be631ce5610ff60aaf291910cce9f119bc2 Mon Sep 17 00:00:00 2001 From: Fabian Date: Sun, 16 Jul 2023 19:03:33 +0200 Subject: [PATCH] Check ssl cert for remote hosts. --- src/Launcher.cs | 51 +++++++++++++++++++++++++++++++++++++++++++-- src/Misc/Helpers.cs | 9 ++++---- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/Launcher.cs b/src/Launcher.cs index ab452d8..a0a827c 100644 --- a/src/Launcher.cs +++ b/src/Launcher.cs @@ -2,6 +2,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.CommandLine.Parsing; +using System.Net.Security; +using System.Net.Sockets; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; using static Arctium.WoW.Launcher.Misc.Helpers; namespace Arctium.WoW.Launcher; @@ -87,14 +91,17 @@ static class Launcher } var configPath = $"{gameFolder}/WTF/{commandLineResult.GetValueForOption(LaunchOptions.GameConfig)}"; + (string IPAddress, string HostName, int Port) portal = new(); if (!File.Exists(configPath)) LaunchOptions.IsDevModeAllowed = false; else { var config = File.ReadAllText(configPath); + + portal = ParsePortal(config); - LaunchOptions.IsDevModeAllowed = IsDevModeAllowed(ipFilter, config); + LaunchOptions.IsDevModeAllowed = IsDevModeAllowed(ipFilter, portal.IPAddress); } if (!LaunchOptions.IsDevModeAllowed) @@ -106,6 +113,46 @@ static class Launcher Console.WriteLine($"Developer mode: {(devModeEnabled ? "Enabled" : "Disabled")}"); Console.WriteLine(); Console.ForegroundColor = ConsoleColor.Gray; + + // Check for valid certificate when dev mode is disabled. + if (!devModeEnabled) + { + try + { + var tcpClient = new TcpClient(portal.HostName, portal.Port); + var sslStream = new SslStream(tcpClient.GetStream(), false, + (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) => + { + if (sslPolicyErrors == SslPolicyErrors.None) + return true; + + // Redirect to the trusted cert warning. + throw new AuthenticationException(); + }, + null + ); + + sslStream.AuthenticateAsClient("portal.HostName"); + } + catch (SocketException) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"{portal.HostName}:{portal.Port} is offline."); + Console.ResetColor(); + + return string.Empty; + } + catch (AuthenticationException) + { + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine($"Server with host name {portal.HostName} does not have a trusted certificate attached."); + Console.WriteLine("If you are the server owner be sure to generate one and replace the default bnet server certificate."); + Console.WriteLine("One way to generate one is through Let's Encrypt."); + Console.ResetColor(); + + return string.Empty; + } + } return gameBinaryPath; } @@ -314,7 +361,7 @@ static class Launcher return false; } - static bool IsDevModeAllowed(IPFilter ipFilter, string config) => ipFilter.IsInRange(ParsePortal(config)); + static bool IsDevModeAllowed(IPFilter ipFilter, string portalIP) => ipFilter.IsInRange(portalIP); static long GenerateAuthSeedFunctionPatch(WinMemory memory, long modulusOffset) { diff --git a/src/Misc/Helpers.cs b/src/Misc/Helpers.cs index 8e3bc3c..8804bf8 100644 --- a/src/Misc/Helpers.cs +++ b/src/Misc/Helpers.cs @@ -45,7 +45,7 @@ static class Helpers Console.WriteLine($"Operating System: {RuntimeInformation.OSDescription}"); } - public static ReadOnlySpan ParsePortal(string config) + public static (string IPAddress, string HostName, int Port) ParsePortal(string config) { const string portalKey = "SET portal"; @@ -68,25 +68,26 @@ static class Helpers var portalSpan = config.AsSpan(startQuoteIndex + 1, portalLength); var colonIndex = portalSpan.IndexOf(':'); var ipSpan = colonIndex != -1 ? portalSpan[..colonIndex] : portalSpan; + var port = colonIndex != -1 ? int.Parse(portalSpan[colonIndex..]) : 1119; var portalString = ipSpan.ToString().Trim(); try { if (IPAddress.TryParse(portalString, out var ipAddress)) - return ipAddress.ToString().AsSpan(); + return (ipAddress.ToString(), portalString, port); var ipv4Address = Dns.GetHostAddresses(portalString).FirstOrDefault(a => a.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork); if (ipv4Address == null) throw new Exception("No IPv4 address found for the provided hostname."); - return ipv4Address.ToString().AsSpan(); + return (ipv4Address.ToString(), portalString, port); } catch (SocketException) { Console.WriteLine("No valid portal found. Dev (Local) mode disabled."); - return string.Empty.AsSpan(); + return (string.Empty, string.Empty, port); } } }