diff --git a/ClientServerLib/ClientServerLib/CServer.cs b/ClientServerLib/ClientServerLib/CServer.cs index ab704a95..51a6d2b7 100644 --- a/ClientServerLib/ClientServerLib/CServer.cs +++ b/ClientServerLib/ClientServerLib/CServer.cs @@ -142,14 +142,14 @@ namespace ServerLib smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15; smb.HttpGetEnabled = true; host.Description.Behaviors.Add(smb);*/ - _RegisterUrlAndCert(_BaseAddress.Port); + _RegisterUrlAndCert(_BaseAddress.Port, false); _Host.Open(); } catch (CommunicationException e) { if (e is AddressAccessDeniedException || e is AddressAlreadyInUseException) { - _RegisterUrlAndCert(_BaseAddress.Port); + _RegisterUrlAndCert(_BaseAddress.Port, true); try { _Host.Abort(); @@ -178,18 +178,18 @@ namespace ServerLib } } - private void _RegisterUrlAndCert(int port) + private void _RegisterUrlAndCert(int port, bool reserve) { #if WIN ProcessStartInfo info = new ProcessStartInfo { FileName = "VocaluxeServerConfig.exe", - Arguments = port + " " + (_Encrypted ? "true" : "false"), + WorkingDirectory = Environment.CurrentDirectory, + Arguments = port + " " + (_Encrypted ? "true" : "false") + (reserve ? " reserve" : ""), UseShellExecute = true, CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden, - Verb = "runas" }; Process p = Process.Start(info); p.WaitForExit(); diff --git a/Vocaluxe/Program.cs b/Vocaluxe/Program.cs index 5d254c13..b59d4f9e 100644 --- a/Vocaluxe/Program.cs +++ b/Vocaluxe/Program.cs @@ -259,6 +259,7 @@ namespace Vocaluxe Environment.Exit(Environment.ExitCode); } +#if !DEBUG [HandleProcessCorruptedStateExceptions] private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) { @@ -273,6 +274,7 @@ namespace Vocaluxe CLog.LogError("Unhandled exception: " + e.Message + stackTrace); _CloseProgram(); } +#endif private static Assembly _AssemblyResolver(Object sender, ResolveEventArgs args) { diff --git a/WebserverInitalConfig/CConfigHttpApi.cs b/WebserverInitalConfig/CConfigHttpApi.cs index 4bbd3ed9..fe184af4 100644 --- a/WebserverInitalConfig/CConfigHttpApi.cs +++ b/WebserverInitalConfig/CConfigHttpApi.cs @@ -15,6 +15,9 @@ // along with Vocaluxe. If not, see . #endregion +using System.Diagnostics; +using System.Security.Authentication; +using System.Security.Principal; using Security.Cryptography; using Security.Cryptography.X509Certificates; using System; @@ -26,34 +29,64 @@ using NetFwTypeLib; namespace WebserverInitalConfig { - public static class CConfigHttpApi + public class CConfigHttpApi { // ReSharper disable InconsistentNaming private const String CLSID_NetFwMgr = "{304CE942-6E39-40D8-943A-B913C40C9CD4}"; private const String CLSID_NetAuthApp = "{EC9846B3-2762-4A6B-A214-6ACB603462D2}"; private const String CLSID_NetOpenPort = "{0CA545C6-37AD-4A6C-BF92-9F7610067EF5}"; + private const string _AppGUID = "{7baf41f1-48b7-42cb-b145-f815499cb48f}"; // ReSharper restore InconsistentNaming - public static void ReserveUrl(string networkString) + + private readonly string _RuleName; + private readonly string _ExePath; + private readonly string _IP; + private readonly int _Port; + private readonly bool _IsTCP; + + public CConfigHttpApi(string ruleName, string exePath, string ip, int port, bool isTCP) { + _ExePath = exePath; + _Port = port; + _IsTCP = isTCP; + _IP = ip; + _RuleName = ruleName; + } + + private static void _ReserveUrl(string networkString) + { + if (!IsAdministrator()) + throw new AuthenticationException(); HttpApi.ReserveURL(networkString, "D:(A;;GX;;;S-1-1-0)"); } - public static void BindCert(string ip, int port, X509Certificate2 cert) + public void ReserveUrl() { - IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse(ip), port); - if (HttpApi.QuerySslCertificateInfo(endpoint) != null) - HttpApi.DeleteCertificateBinding(endpoint); - - HttpApi.BindCertificate(endpoint, cert.GetCertHash(), StoreName.My, new Guid("{7baf41f1-48b7-42cb-b145-f815499cb48f}")); + _ReserveUrl("https://+:" + _Port + "/"); } - public static void AddCertToStore(X509Certificate2 cert) + private void _BindCert(X509Certificate cert) + { + IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse(_IP), _Port); + HttpApi.SslCertificateInfo certificateInfo = HttpApi.QuerySslCertificateInfo(endpoint); + if (certificateInfo != null && !certificateInfo.Hash.SequenceEqual(cert.GetCertHash())) + { + if (!IsAdministrator()) + throw new AuthenticationException(); + HttpApi.DeleteCertificateBinding(endpoint); + + HttpApi.BindCertificate(endpoint, cert.GetCertHash(), StoreName.My, new Guid(_AppGUID)); + } + } + + private static void _AddCertToStore(X509Certificate2 cert) { X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine); store.Open(OpenFlags.ReadWrite); - X509Certificate2Collection oldCerts = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, - cert.SubjectName.Name, false); + // ReSharper disable AssignNullToNotNullAttribute + X509Certificate2Collection oldCerts = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, cert.SubjectName.Name, false); + // ReSharper restore AssignNullToNotNullAttribute foreach (X509Certificate2 oldCert in oldCerts) store.Remove(oldCert); @@ -62,7 +95,25 @@ namespace WebserverInitalConfig store.Close(); } - public static X509Certificate2 GetSelfSignedCert(string subjectName) + private static X509Certificate2 _GetCert(string name) + { + X509Certificate2 result = null; + X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine); + store.Open(OpenFlags.ReadOnly); + + var name2 = new X500DistinguishedName(name); + // ReSharper disable AssignNullToNotNullAttribute + X509Certificate2Collection certs = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, name2.Name, false); + // ReSharper restore AssignNullToNotNullAttribute + if (certs.Count > 0) + result = certs[0]; + + store.Close(); + + return result; + } + + private X509Certificate2 _GetSelfSignedCert(string subjectName) { var keyParam = new CngKeyCreationParameters { @@ -91,58 +142,113 @@ namespace WebserverInitalConfig byte[] rawData = key.CreateSelfSignedCertificate(param).Export(X509ContentType.Pfx, ""); var cert = new X509Certificate2(rawData, "", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet) { - FriendlyName = "Vocaluxe Server Certificate" + FriendlyName = _RuleName + " Server Certificate" }; return cert; } - private static void _AddFirewallRuleForProfile(INetFwProfile profile, string exePath, int port, bool isTCP) + public void CreateAndAddCert(string hostName) { - bool isFirewallEnabled = profile.FirewallEnabled; + string certName = "CN=" + hostName + ", C=DE, O=" + _RuleName + ", OU=" + _RuleName + " Server"; - if (!isFirewallEnabled) - return; - INetFwAuthorizedApplications applications = profile.AuthorizedApplications; - if (!(from INetFwAuthorizedApplication a in applications - where a.ProcessImageFileName == exePath - select a).Any()) + X509Certificate2 cert = _GetCert(certName); + if (cert == null) { - INetFwAuthorizedApplication application = (INetFwAuthorizedApplication)Activator.CreateInstance( - Type.GetTypeFromCLSID(new Guid(CLSID_NetAuthApp))); - - application.Name = "Vocaluxe"; - application.ProcessImageFileName = exePath; - application.Enabled = true; - application.Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL; - applications.Add(application); - } - INetFwOpenPorts openPorts = profile.GloballyOpenPorts; - NET_FW_IP_PROTOCOL_ protocol = isTCP ? NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP : NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_UDP; - if (!(from INetFwOpenPort p in openPorts - where (p.Port == port && - (p.Protocol == protocol)) - select p).Any()) - { - INetFwOpenPort openPort = (INetFwOpenPort)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid(CLSID_NetOpenPort))); - openPort.Enabled = true; - openPort.Port = port; - openPort.Protocol = protocol; - openPort.Name = "Vocaluxe(" + port + ")"; - openPort.Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL; - - openPorts.Add(openPort); + if (!IsAdministrator()) + throw new AuthenticationException(); + cert = _GetSelfSignedCert(certName); + _AddCertToStore(cert); } + _BindCert(cert); } - public static void AddFirewallRule(string exePath, int port, bool isTCP) + private bool _IsAppAuthorized(INetFwProfile profile) + { + if (!profile.FirewallEnabled) + return true; + foreach (INetFwAuthorizedApplication a in profile.AuthorizedApplications) + { + if (a.ProcessImageFileName == _ExePath) + return true; + } + return false; + } + + private bool _IsPortOpen(INetFwProfile profile) + { + NET_FW_IP_PROTOCOL_ protocol = _GetProtocol(); + + if (!profile.FirewallEnabled) + return true; + foreach (INetFwOpenPort p in profile.GloballyOpenPorts) + { + if (p.Protocol == protocol && p.Port == _Port) + return true; + } + return false; + } + + private NET_FW_IP_PROTOCOL_ _GetProtocol() + { + return _IsTCP ? NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP : NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_UDP; + } + + private void _AddFirewallRuleForProfile(INetFwProfile profile) + { + bool isAppAuthorized = _IsAppAuthorized(profile); + bool isPortOpen = _IsPortOpen(profile); + + if ((!isAppAuthorized || !isPortOpen) && !IsAdministrator()) + throw new AuthenticationException(); + if (!isAppAuthorized) + _AddAppToFirewall(profile); + + if (!isPortOpen) + _AddPortToFirewall(profile); + } + + private void _AddPortToFirewall(INetFwProfile profile) + { + INetFwOpenPort openPort = (INetFwOpenPort)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid(CLSID_NetOpenPort))); + openPort.Enabled = true; + openPort.Port = _Port; + openPort.Protocol = _GetProtocol(); + openPort.Name = _RuleName + "(" + _Port + ")"; + openPort.Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL; + + profile.GloballyOpenPorts.Add(openPort); + } + + private void _AddAppToFirewall(INetFwProfile profile) + { + INetFwAuthorizedApplication application = (INetFwAuthorizedApplication)Activator.CreateInstance( + Type.GetTypeFromCLSID(new Guid(CLSID_NetAuthApp))); + + application.Name = _RuleName; + application.ProcessImageFileName = _ExePath; + application.Enabled = true; + application.Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL; + profile.AuthorizedApplications.Add(application); + } + + public static bool IsAdministrator() + { + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + // ReSharper disable AssignNullToNotNullAttribute + WindowsPrincipal principal = new WindowsPrincipal(identity); + // ReSharper restore AssignNullToNotNullAttribute + return principal.IsInRole(WindowsBuiltInRole.Administrator); + } + + public void AddFirewallRule() { INetFwMgr manage = (INetFwMgr)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid(CLSID_NetFwMgr))); - //AddFirewallRuleForProfile(manage.LocalPolicy.CurrentProfile, exePath, port, isTCP); + //_AddFirewallRuleForProfile(manage.LocalPolicy.CurrentProfile); //if (manage.CurrentProfileType != NET_FW_PROFILE_TYPE_.NET_FW_PROFILE_STANDARD) //Add to Home/Work(Private) and current net //TODO: This somewhow adds 2 entries each instead of only one that is activated for Home/Work and public nets - _AddFirewallRuleForProfile(manage.LocalPolicy.GetProfileByType(NET_FW_PROFILE_TYPE_.NET_FW_PROFILE_STANDARD), exePath, port, isTCP); - _AddFirewallRuleForProfile(manage.LocalPolicy.GetProfileByType(NET_FW_PROFILE_TYPE_.NET_FW_PROFILE_CURRENT), exePath, port, isTCP); + _AddFirewallRuleForProfile(manage.LocalPolicy.GetProfileByType(NET_FW_PROFILE_TYPE_.NET_FW_PROFILE_STANDARD)); + _AddFirewallRuleForProfile(manage.LocalPolicy.GetProfileByType(NET_FW_PROFILE_TYPE_.NET_FW_PROFILE_CURRENT)); } /*public static X509Certificate2 getCert(string subject) diff --git a/WebserverInitalConfig/Program.cs b/WebserverInitalConfig/Program.cs index 4b7ce630..a4f3a315 100644 --- a/WebserverInitalConfig/Program.cs +++ b/WebserverInitalConfig/Program.cs @@ -16,34 +16,64 @@ #endregion using System; +using System.Diagnostics; using System.IO; using System.Net; +using System.Net.Mime; +using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; namespace WebserverInitalConfig { - class Program + static class CProgram { + // ReSharper disable InconsistentNaming private static void Main(string[] args) + // ReSharper restore InconsistentNaming { - if (args.Length != 2) + int port; + if (args.Length < 2 || !int.TryParse(args[0], out port)) { Console.WriteLine("Usage: VocaluxeServerConfig port isHttps"); Environment.Exit(-1); } - - CConfigHttpApi.AddFirewallRule(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Vocaluxe.exe"), Int32.Parse(args[0]), true); - - if (args[1].ToLower() == "true") - { - string hostname = Dns.GetHostName(); - X509Certificate2 cert = CConfigHttpApi.GetSelfSignedCert("CN=" + hostname + " C=DE O=Vocaluxe OU=Vocaluxe Server"); - CConfigHttpApi.AddCertToStore(cert); - CConfigHttpApi.BindCert("0.0.0.0", Int32.Parse(args[0]), cert); - CConfigHttpApi.ReserveUrl("https://+:" + args[0] + "/"); - } else - CConfigHttpApi.ReserveUrl("http://+:" + args[0] + "/"); + { + CConfigHttpApi config = new CConfigHttpApi("Vocaluxe", Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Vocaluxe.exe"), "0.0.0.0", port, true); + try + { + config.AddFirewallRule(); + if (args[1].ToLower() == "true") + config.CreateAndAddCert(Dns.GetHostName()); + + if ((args.Length >= 3 && args[2] == "reserve") || CConfigHttpApi.IsAdministrator()) + config.ReserveUrl(); + } + catch (AuthenticationException) + { + ProcessStartInfo proc = new ProcessStartInfo + { + UseShellExecute = true, + WorkingDirectory = Environment.CurrentDirectory, + FileName = Process.GetCurrentProcess().MainModule.FileName, + Arguments = args[0] + " " + args[1], + Verb = "runas", + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + }; + + try + { + Process.Start(proc); + } + catch + { + // The user refused the elevation. + // Do nothing and return directly ... + Environment.Exit(-1); + } + } + } } } } \ No newline at end of file diff --git a/WebserverInitalConfig/WebserverInitalConfig.csproj b/WebserverInitalConfig/WebserverInitalConfig.csproj index 7544f7a3..2f48f927 100644 --- a/WebserverInitalConfig/WebserverInitalConfig.csproj +++ b/WebserverInitalConfig/WebserverInitalConfig.csproj @@ -32,9 +32,6 @@ prompt 4 - - app.manifest - Vocaluxe.ico